@nowramp/sdk 0.1.28 → 0.1.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -3,4 +3,4 @@
3
3
  * (c) 2026 NowRamp
4
4
  * @license MIT
5
5
  */
6
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e={evm:{pattern:/^0x[a-fA-F0-9]{40}$/,description:"EVM address must start with 0x followed by 40 hexadecimal characters"},solana:{pattern:/^[1-9A-HJ-NP-Za-km-z]{32,44}$/,description:"Solana address must be 32-44 base58 characters"},bitcoin:{pattern:/^(1[a-km-zA-HJ-NP-Z1-9]{25,34}|3[a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})$/,description:"Bitcoin address must be a valid legacy, SegWit, or native SegWit format"},tezos:{pattern:/^(tz[123]|KT1)[a-zA-Z0-9]{33}$/,description:"Tezos address must start with tz1/tz2/tz3 (implicit) or KT1 (contract)"},other:{pattern:/^.{10,100}$/,description:"Address format varies by network (10-100 characters)"}},t=["https:"],r=["localhost","127.0.0.1"],s={public:"pk_",secret:"sk_"};function i(e){return e?e.includes(s.secret)?{valid:!1,error:"Secret API keys (sk_) cannot be used in the SDK. Use a public key (pk_) instead. Secret keys should only be used in backend server code."}:(e.includes(s.public)||console.warn("RampWidget: API key does not have a public key prefix (pk_). For security, please generate a new public key for frontend use."),{valid:!0}):{valid:!1,error:"apiKey is required"}}function o(e){if(!e)return{valid:!0};try{const s=new URL(e);return t.includes(s.protocol)||"http:"===s.protocol&&r.includes(s.hostname)?{valid:!0}:{valid:!1,error:`redirectUrl must use HTTPS (got ${s.protocol})`}}catch{return{valid:!1,error:"redirectUrl is not a valid URL"}}}function n(t,r){if(!t)return{valid:!0};const s=t.trim();if(r){const t=r.toLowerCase(),i=e[t];if(i){const{pattern:e,description:t}=i;return e.test(s)?{valid:!0}:{valid:!1,error:t}}return s.length>=10&&s.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}return s.length>=10&&s.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}function a(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const r=new URL(e);return"localhost"===r.hostname||"127.0.0.1"===r.hostname}catch{return!1}}class c{constructor(e){this.iframe=null,this.modalContainer=null,this.boundMessageHandler=null,this.isOpen=!1,this.config={widgetUrl:e.widgetUrl||"https://widget.nowramp.com/widget/latest/index.html",apiUrl:e.apiUrl||"https://api.nowramp.com",flowType:"buy",theme:"light",...e},this.validateConfig()}open(){this.isOpen?console.warn("RampWidget: Widget is already open"):(this.isOpen=!0,this.setupMessageListener(),this.createIframe())}close(){this.isOpen&&(this.isOpen=!1,this.cleanup(),this.config.onClose?.())}setAmount(e){this.sendCommand("SET_AMOUNT",e)}setTheme(e){this.sendCommand("SET_THEME",e)}refreshQuote(){this.sendCommand("REFRESH_QUOTE")}validateConfig(){if(!this.config.apiKey)throw new Error("RampWidget: apiKey is required");if(!this.config.projectId)throw new Error("RampWidget: projectId is required");if(!!(!this.config.sessionId||!this.config.clientSecret)&&!this.config.externalUserId)throw new Error("RampWidget: Either externalUserId (legacy) or sessionId + clientSecret (recommended) is required");if(this.config.sessionId&&!this.config.clientSecret)throw new Error("RampWidget: clientSecret is required when using sessionId");const e=i(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=o(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=n(this.config.walletAddress,void 0);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return this.config.sessionId&&this.config.clientSecret&&(e.set("sessionId",this.config.sessionId),e.set("clientSecret",this.config.clientSecret)),this.config.externalUserId&&e.set("externalUserId",this.config.externalUserId),this.config.apiUrl&&e.set("apiUrl",this.config.apiUrl),this.config.locale&&e.set("locale",this.config.locale),void 0!==this.config.amount&&e.set("amount",String(this.config.amount)),this.config.sourceCurrency&&e.set("sourceCurrency",this.config.sourceCurrency),this.config.destinationCurrency&&e.set("destinationCurrency",this.config.destinationCurrency),this.config.walletAddress&&e.set("walletAddress",this.config.walletAddress),this.config.network&&e.set("network",this.config.network),this.config.redirectUrl&&e.set("redirectUrl",this.config.redirectUrl),!1===this.config.autoRedirect&&e.set("autoRedirect","false"),`${this.config.widgetUrl}?${e.toString()}`}createIframe(){this.iframe=document.createElement("iframe"),this.iframe.src=this.buildWidgetUrl(),this.iframe.style.border="none",this.iframe.style.width="100%",this.iframe.style.height="100%",this.iframe.allow="camera *; microphone *; geolocation *; encrypted-media *; clipboard-read; clipboard-write; payment *",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-popups-without-user-activation allow-top-navigation allow-downloads");const e=this.getContainer();e?e.appendChild(this.iframe):this.createModal()}getContainer(){return this.config.container?"string"==typeof this.config.container?document.querySelector(this.config.container):this.config.container:null}createModal(){this.modalContainer=document.createElement("div"),this.modalContainer.style.cssText="\n position: fixed;\n inset: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n ";const e="dark"===this.config.theme,t=document.createElement("div");t.style.cssText=`\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: ${e?"#000000":"white"};\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n `,this.iframe&&t.appendChild(this.iframe),this.modalContainer.appendChild(t),this.modalContainer.onclick=e=>{e.target===this.modalContainer&&this.close()};document.addEventListener("keydown",e=>{"Escape"===e.key&&this.close()}),document.body.appendChild(this.modalContainer),document.body.style.overflow="hidden"}setupMessageListener(){this.boundMessageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.boundMessageHandler)}handleMessage(e){if(!a(e.origin,this.config.widgetUrl))return;const t=e.data;if(t&&"ramp-widget"===t.source)switch(t.type){case"WIDGET_READY":this.config.onReady?.();break;case"WIDGET_CLOSE":this.close();break;case"STEP_CHANGE":this.config.onStepChange?.(t.payload.step);break;case"QUOTE_CREATED":this.config.onQuoteCreated?.(t.payload);break;case"ORDER_CREATED":this.config.onOrderCreated?.(t.payload);break;case"ORDER_COMPLETED":this.config.onSuccess?.(t.payload);break;case"ERROR":this.config.onError?.(t.payload)}}sendCommand(e,t){if(!this.iframe?.contentWindow)return void console.warn("RampWidget: Cannot send command, widget not open");const r=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},r)}cleanup(){this.boundMessageHandler&&(window.removeEventListener("message",this.boundMessageHandler),this.boundMessageHandler=null),this.iframe?.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.modalContainer?.parentNode&&(this.modalContainer.parentNode.removeChild(this.modalContainer),document.body.style.overflow=""),this.modalContainer=null}}class d{constructor(e){this.config=e,this.baseUrl=e.apiUrl||"https://api.nowramp.com"}async getRate(e){if(!this.config.apiKey)return this.getPublicRate(e);const t=await fetch(`${this.baseUrl}/v1/rates/convert`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{rate:s.rate,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationAmount:s.destinationAmount,destinationCurrency:s.destinationCurrency,fees:{processingFee:s.fees?.processingFee||0,processingFeePercent:s.fees?.processingFeePercent||0,networkFee:s.fees?.networkFee||0,totalFee:s.fees?.totalFee||0},provider:s.provider||"unknown",expiresAt:s.expiresAt}}async getPublicRate(e){const t=await fetch(`${this.baseUrl}/public/wert/convert`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{rate:s.ticker||s.rate,sourceAmount:s.currencyAmount||s.sourceAmount||e.amount,sourceCurrency:e.from,destinationAmount:s.commodityAmount||s.destinationAmount,destinationCurrency:e.to,fees:{processingFee:s.feeAmount||s.fees?.processingFee||0,processingFeePercent:s.feePercent||s.fees?.processingFeePercent||0,networkFee:s.currencyMinerFee||s.fees?.networkFee||0,totalFee:(s.feeAmount||0)+(s.currencyMinerFee||0)||s.fees?.totalFee||0},provider:"wert",expiresAt:s.expiresAt}}async getCurrencies(){if(!this.config.apiKey)return this.getPublicCurrencies();const e=await fetch(`${this.baseUrl}/v1/rates/currencies`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${r}`)}const t=await e.json(),r=t.data?.attributes||t.data||t;return{fiat:r.fiat||[],crypto:r.crypto||[],provider:r.provider||"unknown",environment:r.environment||"sandbox"}}async getPublicCurrencies(){if(!this.config.projectId)throw new Error("projectId is required to fetch currencies without an API key");const e=await fetch(`${this.baseUrl}/public/projects/${this.config.projectId}/currencies`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${r}`)}const t=await e.json(),r=t.data?.attributes||t.data||t;return{fiat:r.fiat||[],crypto:r.crypto||[],provider:r.provider||"unknown",environment:r.environment||"sandbox"}}async validateAddress(e){const t=await fetch(`${this.baseUrl}/public/validate/address`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate address: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async validateAddressBatch(e){if(e.length>20)throw new Error("Maximum 20 addresses per batch");const t=await fetch(`${this.baseUrl}/public/validate/address/batch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({addresses:e})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate addresses: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async getSupportedNetworks(){const e=await fetch(`${this.baseUrl}/public/validate/networks`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get supported networks: ${r}`)}const t=await e.json();return t.data?.attributes||t.data||t}async createSession(e){if(!this.config.apiKey)throw new Error("API key is required to create sessions. Use a secret key (sk_) for this operation.");const t=await fetch(`${this.baseUrl}/v1/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify(e)});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to create session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,clientSecret:s.clientSecret,type:s.type||"onramp",status:s.status||"pending",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,expiresAt:s.expiresAt,createdAt:s.createdAt}}async getSession(e){if(!this.config.apiKey)throw new Error("API key is required to get session details.");const t=await fetch(`${this.baseUrl}/v1/sessions/${e}`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,type:s.type||"onramp",status:s.status||"pending",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,fieldLocks:s.fieldLocks,orderId:s.orderId,expiresAt:s.expiresAt,confirmedAt:s.confirmedAt,completedAt:s.completedAt,createdAt:s.createdAt}}async cancelSession(e){if(!this.config.apiKey)throw new Error("API key is required to cancel sessions. Use a secret key (sk_) for this operation.");const t=await fetch(`${this.baseUrl}/v1/sessions/${e}/cancel`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to cancel session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,type:s.type||"onramp",status:s.status||"cancelled",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,orderId:s.orderId,expiresAt:s.expiresAt,confirmedAt:s.confirmedAt,completedAt:s.completedAt,createdAt:s.createdAt}}}exports.ADDRESS_PATTERNS=e,exports.ALLOWED_PROTOCOLS=t,exports.API_KEY_PREFIXES=s,exports.DEV_ALLOWED_HOSTNAMES=r,exports.RampApi=d,exports.RampWidget=c,exports.VERSION="0.1.13",exports.default=c,exports.getCurrencies=async function(e){return new d(e).getCurrencies()},exports.getRate=async function(e,t){return new d(e).getRate(t)},exports.getSupportedNetworks=async function(e){return new d({apiKey:"",projectId:"",...e}).getSupportedNetworks()},exports.validateAddress=async function(e,t){return new d({apiKey:"",projectId:"",...e}).validateAddress(t)},exports.validateAddressBatch=async function(e,t){return new d({apiKey:"",projectId:"",...e}).validateAddressBatch(t)},exports.validateApiKey=i,exports.validateMessageOrigin=a,exports.validateRedirectUrl=o,exports.validateWalletAddress=n;
6
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e={evm:{pattern:/^0x[a-fA-F0-9]{40}$/,description:"EVM address must start with 0x followed by 40 hexadecimal characters"},solana:{pattern:/^[1-9A-HJ-NP-Za-km-z]{32,44}$/,description:"Solana address must be 32-44 base58 characters"},bitcoin:{pattern:/^(1[a-km-zA-HJ-NP-Z1-9]{25,34}|3[a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})$/,description:"Bitcoin address must be a valid legacy, SegWit, or native SegWit format"},tezos:{pattern:/^(tz[123]|KT1)[a-zA-Z0-9]{33}$/,description:"Tezos address must start with tz1/tz2/tz3 (implicit) or KT1 (contract)"},other:{pattern:/^.{10,100}$/,description:"Address format varies by network (10-100 characters)"}},t=["https:"],r=["localhost","127.0.0.1"],s={public:"pk_",secret:"sk_"};function o(e){return e?e.includes(s.secret)?{valid:!1,error:"Secret API keys (sk_) cannot be used in the SDK. Use a public key (pk_) instead. Secret keys should only be used in backend server code."}:(e.includes(s.public)||console.warn("RampWidget: API key does not have a public key prefix (pk_). For security, please generate a new public key for frontend use."),{valid:!0}):{valid:!1,error:"apiKey is required"}}function n(e){if(!e)return{valid:!0};try{const s=new URL(e);return t.includes(s.protocol)||"http:"===s.protocol&&r.includes(s.hostname)?{valid:!0}:{valid:!1,error:`redirectUrl must use HTTPS (got ${s.protocol})`}}catch{return{valid:!1,error:"redirectUrl is not a valid URL"}}}function i(t,r){if(!t)return{valid:!0};const s=t.trim();if(r){const t=r.toLowerCase(),o=e[t];if(o){const{pattern:e,description:t}=o;return e.test(s)?{valid:!0}:{valid:!1,error:t}}return s.length>=10&&s.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}return s.length>=10&&s.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}function a(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const r=new URL(e);return"localhost"===r.hostname||"127.0.0.1"===r.hostname}catch{return!1}}class c{constructor(e){this.iframe=null,this.modalContainer=null,this.boundMessageHandler=null,this.isOpen=!1,this.config={widgetUrl:e.widgetUrl||"https://widget.nowramp.com/widget/latest/index.html",apiUrl:e.apiUrl||"https://api.nowramp.com",flowType:"buy",theme:"light",...e},this.validateConfig()}open(){this.isOpen?console.warn("RampWidget: Widget is already open"):(this.isOpen=!0,this.setupMessageListener(),this.createIframe())}close(){this.isOpen&&(this.isOpen=!1,this.cleanup(),this.config.onClose?.())}setAmount(e){this.sendCommand("SET_AMOUNT",e)}setTheme(e){this.sendCommand("SET_THEME",e)}refreshQuote(){this.sendCommand("REFRESH_QUOTE")}validateConfig(){if(!this.config.apiKey)throw new Error("RampWidget: apiKey is required");if(!this.config.projectId)throw new Error("RampWidget: projectId is required");if(!!(!this.config.sessionId||!this.config.clientSecret)&&!this.config.externalUserId)throw new Error("RampWidget: Either externalUserId (legacy) or sessionId + clientSecret (recommended) is required");if(this.config.sessionId&&!this.config.clientSecret)throw new Error("RampWidget: clientSecret is required when using sessionId");const e=o(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=n(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=i(this.config.walletAddress,void 0);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return this.config.sessionId&&this.config.clientSecret&&(e.set("sessionId",this.config.sessionId),e.set("clientSecret",this.config.clientSecret)),this.config.externalUserId&&e.set("externalUserId",this.config.externalUserId),this.config.apiUrl&&e.set("apiUrl",this.config.apiUrl),this.config.locale&&e.set("locale",this.config.locale),void 0!==this.config.amount&&e.set("amount",String(this.config.amount)),this.config.sourceCurrency&&e.set("sourceCurrency",this.config.sourceCurrency),this.config.destinationCurrency&&e.set("destinationCurrency",this.config.destinationCurrency),this.config.walletAddress&&e.set("walletAddress",this.config.walletAddress),this.config.network&&e.set("network",this.config.network),this.config.redirectUrl&&e.set("redirectUrl",this.config.redirectUrl),!1===this.config.autoRedirect&&e.set("autoRedirect","false"),`${this.config.widgetUrl}?${e.toString()}`}createIframe(){this.iframe=document.createElement("iframe"),this.iframe.src=this.buildWidgetUrl(),this.iframe.style.border="none",this.iframe.style.width="100%",this.iframe.style.height="100%",this.iframe.allow="camera *; microphone *; geolocation *; encrypted-media *; clipboard-read; clipboard-write; payment *",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-popups-without-user-activation allow-top-navigation allow-downloads");const e=this.getContainer();e?e.appendChild(this.iframe):this.createModal()}getContainer(){return this.config.container?"string"==typeof this.config.container?document.querySelector(this.config.container):this.config.container:null}createModal(){this.modalContainer=document.createElement("div"),this.modalContainer.style.cssText="\n position: fixed;\n inset: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n ";const e="dark"===this.config.theme,t=document.createElement("div");t.style.cssText=`\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: ${e?"#000000":"white"};\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n `,this.iframe&&t.appendChild(this.iframe),this.modalContainer.appendChild(t),this.modalContainer.onclick=e=>{e.target===this.modalContainer&&this.close()};document.addEventListener("keydown",e=>{"Escape"===e.key&&this.close()}),document.body.appendChild(this.modalContainer),document.body.style.overflow="hidden"}setupMessageListener(){this.boundMessageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.boundMessageHandler)}handleMessage(e){if(!a(e.origin,this.config.widgetUrl))return;const t=e.data;if(t&&"ramp-widget"===t.source)switch(t.type){case"WIDGET_READY":this.config.onReady?.();break;case"WIDGET_CLOSE":this.close();break;case"STEP_CHANGE":this.config.onStepChange?.(t.payload.step);break;case"QUOTE_CREATED":this.config.onQuoteCreated?.(t.payload);break;case"ORDER_CREATED":this.config.onOrderCreated?.(t.payload);break;case"ORDER_COMPLETED":this.config.onSuccess?.(t.payload);break;case"ERROR":this.config.onError?.(t.payload)}}sendCommand(e,t){if(!this.iframe?.contentWindow)return void console.warn("RampWidget: Cannot send command, widget not open");const r=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},r)}cleanup(){this.boundMessageHandler&&(window.removeEventListener("message",this.boundMessageHandler),this.boundMessageHandler=null),this.iframe?.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.modalContainer?.parentNode&&(this.modalContainer.parentNode.removeChild(this.modalContainer),document.body.style.overflow=""),this.modalContainer=null}}class d{constructor(e){this.config=e,this.baseUrl=e.apiUrl||"https://api.nowramp.com"}async getRate(e){if(!this.config.apiKey)return this.getPublicRate(e);const t=await fetch(`${this.baseUrl}/v1/rates/convert`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{rate:s.rate,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationAmount:s.destinationAmount,destinationCurrency:s.destinationCurrency,fees:{processingFee:s.fees?.processingFee||0,processingFeePercent:s.fees?.processingFeePercent||0,networkFee:s.fees?.networkFee||0,totalFee:s.fees?.totalFee||0},provider:s.provider||"unknown",expiresAt:s.expiresAt}}async getPublicRate(e){const t=await fetch(`${this.baseUrl}/public/wert/convert`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{rate:s.ticker||s.rate,sourceAmount:s.currencyAmount||s.sourceAmount||e.amount,sourceCurrency:e.from,destinationAmount:s.commodityAmount||s.destinationAmount,destinationCurrency:e.to,fees:{processingFee:s.feeAmount||s.fees?.processingFee||0,processingFeePercent:s.feePercent||s.fees?.processingFeePercent||0,networkFee:s.currencyMinerFee||s.fees?.networkFee||0,totalFee:(s.feeAmount||0)+(s.currencyMinerFee||0)||s.fees?.totalFee||0},provider:"wert",expiresAt:s.expiresAt}}async getCurrencies(){if(!this.config.apiKey)return this.getPublicCurrencies();const e=await fetch(`${this.baseUrl}/v1/rates/currencies`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${r}`)}const t=await e.json(),r=t.data?.attributes||t.data||t;return{fiat:r.fiat||[],crypto:r.crypto||[],provider:r.provider||"unknown",environment:r.environment||"sandbox"}}async getPublicCurrencies(){if(!this.config.projectId)throw new Error("projectId is required to fetch currencies without an API key");const e=await fetch(`${this.baseUrl}/public/projects/${this.config.projectId}/currencies`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${r}`)}const t=await e.json(),r=t.data?.attributes||t.data||t;return{fiat:r.fiat||[],crypto:r.crypto||[],provider:r.provider||"unknown",environment:r.environment||"sandbox"}}async validateAddress(e){const t=await fetch(`${this.baseUrl}/public/validate/address`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate address: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async validateAddressBatch(e){if(e.length>20)throw new Error("Maximum 20 addresses per batch");const t=await fetch(`${this.baseUrl}/public/validate/address/batch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({addresses:e})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate addresses: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async getSupportedNetworks(){const e=await fetch(`${this.baseUrl}/public/validate/networks`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get supported networks: ${r}`)}const t=await e.json();return t.data?.attributes||t.data||t}async createSession(e){if(!this.config.apiKey)throw new Error("API key is required to create sessions. Use a secret key (sk_) for this operation.");const t=await fetch(`${this.baseUrl}/v1/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify(e)});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to create session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,clientSecret:s.clientSecret,type:s.type||"onramp",status:s.status||"pending",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,expiresAt:s.expiresAt,createdAt:s.createdAt}}async getSession(e){if(!this.config.apiKey)throw new Error("API key is required to get session details.");const t=await fetch(`${this.baseUrl}/v1/sessions/${e}`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,type:s.type||"onramp",status:s.status||"pending",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,fieldLocks:s.fieldLocks,orderId:s.orderId,expiresAt:s.expiresAt,confirmedAt:s.confirmedAt,completedAt:s.completedAt,createdAt:s.createdAt}}async cancelSession(e){if(!this.config.apiKey)throw new Error("API key is required to cancel sessions. Use a secret key (sk_) for this operation.");const t=await fetch(`${this.baseUrl}/v1/sessions/${e}/cancel`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to cancel session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,type:s.type||"onramp",status:s.status||"cancelled",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,orderId:s.orderId,expiresAt:s.expiresAt,confirmedAt:s.confirmedAt,completedAt:s.completedAt,createdAt:s.createdAt}}async getSupported(e){if(!this.config.projectId)throw new Error("projectId is required to get supported config");const t=new URLSearchParams({projectId:this.config.projectId});e&&t.set("orderType",e);const r=await fetch(`${this.baseUrl}/public/v1/onramp/supported?${t}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!r.ok){const e=await r.json().catch(()=>({})),t=e.error?.message||e.message||`HTTP ${r.status}`;throw new Error(`Failed to get supported config: ${t}`)}const s=await r.json();return s.data?.attributes||s.data||s}async getQuotes(e){if(!this.config.projectId)throw new Error("projectId is required to get quotes");const t=new URLSearchParams({projectId:this.config.projectId,fiatCurrency:e.fiatCurrency,cryptoCurrency:e.cryptoCurrency,network:e.network});e.fiatAmount&&t.set("fiatAmount",e.fiatAmount),e.cryptoAmount&&t.set("cryptoAmount",e.cryptoAmount),e.orderType&&t.set("orderType",e.orderType),e.paymentMethodId&&t.set("paymentMethodId",e.paymentMethodId),e.country&&t.set("country",e.country);const r=await fetch(`${this.baseUrl}/public/v1/onramp/quotes?${t}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!r.ok){const e=await r.json().catch(()=>({})),t=e.error?.message||e.message||`HTTP ${r.status}`;throw new Error(`Failed to get quotes: ${t}`)}const s=await r.json();return s.data?.attributes||s.data||s}async createCheckoutIntent(e){if(!this.config.projectId)throw new Error("projectId is required to create checkout intents");const t=await fetch(`${this.baseUrl}/public/v1/onramp/checkout-intent`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectId:this.config.projectId,...e})});if(!t.ok){const e=await t.json().catch(()=>({})),r=("string"==typeof e.error?e.error:e.error?.message)||e.message||`HTTP ${t.status}`;let s="";if(e.details)try{const t="string"==typeof e.details?JSON.parse(e.details):e.details;if(t.errors&&"object"==typeof t.errors){const e=Object.values(t.errors).flat().filter(Boolean);e.length&&(s=e.join("; "))}}catch{}throw new Error(`Failed to create checkout intent: ${s||r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async getTransaction(e){const t=await fetch(`${this.baseUrl}/public/v1/onramp/transactions/${e}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get transaction: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}}exports.ADDRESS_PATTERNS=e,exports.ALLOWED_PROTOCOLS=t,exports.API_KEY_PREFIXES=s,exports.DEV_ALLOWED_HOSTNAMES=r,exports.RampApi=d,exports.RampWidget=c,exports.VERSION="0.1.13",exports.createCheckoutIntent=async function(e,t){return new d({apiKey:"",...e}).createCheckoutIntent(t)},exports.default=c,exports.getCurrencies=async function(e){return new d(e).getCurrencies()},exports.getQuotes=async function(e,t){return new d({apiKey:"",...e}).getQuotes(t)},exports.getRate=async function(e,t){return new d(e).getRate(t)},exports.getSupported=async function(e,t){return new d({apiKey:"",...e}).getSupported(t)},exports.getSupportedNetworks=async function(e){return new d({apiKey:"",projectId:"",...e}).getSupportedNetworks()},exports.getTransaction=async function(e,t){return new d({apiKey:"",projectId:"",...e}).getTransaction(t)},exports.validateAddress=async function(e,t){return new d({apiKey:"",projectId:"",...e}).validateAddress(t)},exports.validateAddressBatch=async function(e,t){return new d({apiKey:"",projectId:"",...e}).validateAddressBatch(t)},exports.validateAmount=function(e,t,r,s){if(!e||""===e.trim())return{valid:!1,error:"Amount is required"};const o=parseFloat(e);if(isNaN(o))return{valid:!1,error:"Amount must be a valid number"};if(o<=0)return{valid:!1,error:"Amount must be greater than zero"};const n=s?` ${s}`:"";return o<t?{valid:!1,error:`Minimum amount is ${t}${n}`}:o>r?{valid:!1,error:`Maximum amount is ${r}${n}`}:{valid:!0}},exports.validateApiKey=o,exports.validateMessageOrigin=a,exports.validateRedirectUrl=n,exports.validateWalletAddress=i;
package/dist/index.d.ts CHANGED
@@ -416,6 +416,319 @@ interface SessionWithSecret extends Session {
416
416
  /** Client secret for session confirmation (only returned once) */
417
417
  clientSecret: string;
418
418
  }
419
+ /**
420
+ * Gateway (provider) information
421
+ */
422
+ interface Gateway {
423
+ /** Gateway ID (e.g., 'banxa', 'wert') */
424
+ id: string;
425
+ /** Display name */
426
+ name: string;
427
+ /** Gateway logo URL */
428
+ logo: string;
429
+ /** Whether this gateway is enabled */
430
+ enabled: boolean;
431
+ /** Gateway capabilities */
432
+ features: GatewayFeatures;
433
+ }
434
+ /**
435
+ * Gateway feature flags
436
+ */
437
+ interface GatewayFeatures {
438
+ /** Supports iframe checkout */
439
+ supportsIframe: boolean;
440
+ /** Supports widget checkout */
441
+ supportsWidget: boolean;
442
+ /** Supports buying crypto */
443
+ supportsBuy: boolean;
444
+ /** Supports selling crypto */
445
+ supportsSell: boolean;
446
+ /** Supports recurring purchases */
447
+ supportsRecurring: boolean;
448
+ /** KYC is handled by the gateway */
449
+ kycHandledByProvider: boolean;
450
+ }
451
+ /**
452
+ * Fiat currency with provider-specific details
453
+ */
454
+ interface SupportedFiat {
455
+ /** Currency code (e.g., 'USD') */
456
+ code: string;
457
+ /** Display name (e.g., 'US Dollar') */
458
+ name: string;
459
+ /** Symbol (e.g., '$') */
460
+ symbol: string;
461
+ /** Minimum amount */
462
+ minAmount: string;
463
+ /** Maximum amount */
464
+ maxAmount: string;
465
+ /** Supported payment methods */
466
+ paymentMethods: string[];
467
+ }
468
+ /**
469
+ * Crypto currency with network info
470
+ */
471
+ interface SupportedCrypto {
472
+ /** Currency code (e.g., 'ETH') */
473
+ code: string;
474
+ /** Display name (e.g., 'Ethereum') */
475
+ name: string;
476
+ /** Available networks */
477
+ networks: SupportedNetworkInfo[];
478
+ }
479
+ /**
480
+ * Blockchain network
481
+ */
482
+ interface SupportedNetworkInfo {
483
+ /** Network ID (e.g., 'ethereum', 'polygon') */
484
+ id: string;
485
+ /** Display name (e.g., 'Ethereum Mainnet') */
486
+ name: string;
487
+ /** Minimum amount */
488
+ minAmount: string;
489
+ /** Maximum amount */
490
+ maxAmount: string;
491
+ /** Is the default network for this crypto */
492
+ isDefault: boolean;
493
+ }
494
+ /**
495
+ * Payment method
496
+ */
497
+ interface SupportedPaymentMethod {
498
+ /** Method ID (e.g., 'debit-credit-card') */
499
+ id: string;
500
+ /** Display name */
501
+ name: string;
502
+ /** Description */
503
+ description?: string;
504
+ /** Supported fiat currencies */
505
+ supportedFiats: string[];
506
+ /** Icon URL */
507
+ icon?: string;
508
+ }
509
+ /**
510
+ * Onramp supported configuration (currencies, gateways, payment methods)
511
+ */
512
+ interface OnrampSupported {
513
+ /** Available gateways */
514
+ gateways: Gateway[];
515
+ /** Supported fiat currencies */
516
+ fiats: SupportedFiat[];
517
+ /** Supported crypto currencies */
518
+ cryptos: SupportedCrypto[];
519
+ /** Available payment methods */
520
+ paymentMethods: SupportedPaymentMethod[];
521
+ /** Default gateway ID for this project (e.g., 'banxa') */
522
+ defaultGateway?: string | null;
523
+ }
524
+ /**
525
+ * Quote fee breakdown
526
+ */
527
+ interface QuoteFees {
528
+ /** Processing fee in fiat */
529
+ processingFee: string;
530
+ /** Network/gas fee in fiat */
531
+ networkFee: string;
532
+ /** Total fees in fiat */
533
+ totalFee: string;
534
+ /** Fee as percentage (e.g., '2.5') */
535
+ feePercentage: string;
536
+ }
537
+ /**
538
+ * Quote from a single provider
539
+ */
540
+ interface Quote {
541
+ /** Gateway ID */
542
+ gatewayId: string;
543
+ /** Gateway display name */
544
+ gatewayName: string;
545
+ /** Gateway logo URL */
546
+ gatewayLogo: string;
547
+ /** Whether this gateway can service this request */
548
+ available: boolean;
549
+ /** Reason if unavailable */
550
+ unavailableReason?: string;
551
+ /** Fiat amount */
552
+ fiatAmount: string;
553
+ /** Fiat currency code */
554
+ fiatCurrency: string;
555
+ /** Crypto amount to receive */
556
+ cryptoAmount: string;
557
+ /** Crypto currency code */
558
+ cryptoCurrency: string;
559
+ /** Network ID */
560
+ network: string;
561
+ /** Fee breakdown */
562
+ fees: QuoteFees;
563
+ /** Exchange rate (1 crypto = X fiat) */
564
+ exchangeRate: string;
565
+ /** Estimated processing time (e.g., '5-10 minutes') */
566
+ estimatedTime: string;
567
+ /** KYC requirements: none, light, or full */
568
+ kycRequired: 'none' | 'light' | 'full';
569
+ /** Quote expiry timestamp (ms) */
570
+ expiresAt: number;
571
+ /** Ranking position (1 = best) */
572
+ rank?: number;
573
+ /** Is this the best rate? */
574
+ isBestRate?: boolean;
575
+ /** Is this the recommended (default) gateway? */
576
+ isRecommended?: boolean;
577
+ /** Routing score (0-100) */
578
+ score?: number;
579
+ }
580
+ /**
581
+ * Gateway that couldn't provide a quote
582
+ */
583
+ interface UnavailableGateway {
584
+ /** Gateway ID */
585
+ gatewayId: string;
586
+ /** Gateway name */
587
+ gatewayName: string;
588
+ /** Reason why unavailable */
589
+ reason: string;
590
+ }
591
+ /**
592
+ * Quotes response from all gateways
593
+ */
594
+ interface QuotesResponse {
595
+ /** All available quotes, ranked by crypto amount */
596
+ quotes: Quote[];
597
+ /** Best rate quote */
598
+ bestQuote: Quote | null;
599
+ /** Fastest processing quote */
600
+ fastestQuote: Quote | null;
601
+ /** Lowest fees quote */
602
+ cheapestFees: Quote | null;
603
+ /** Gateways that couldn't quote */
604
+ unavailableGateways: UnavailableGateway[];
605
+ }
606
+ /**
607
+ * Parameters for getting quotes
608
+ */
609
+ interface GetQuotesParams {
610
+ /** Fiat currency code (e.g., 'USD') */
611
+ fiatCurrency: string;
612
+ /** Fiat amount (required for buy, optional for sell) */
613
+ fiatAmount?: string;
614
+ /** Crypto amount (required for sell, optional for buy) */
615
+ cryptoAmount?: string;
616
+ /** Crypto currency code (e.g., 'ETH') */
617
+ cryptoCurrency: string;
618
+ /** Network ID (e.g., 'ethereum') */
619
+ network: string;
620
+ /** Order type: buy (fiat→crypto) or sell (crypto→fiat) */
621
+ orderType?: FlowType;
622
+ /** Payment method ID (optional) */
623
+ paymentMethodId?: string;
624
+ /** Country code (optional, for region-specific pricing) */
625
+ country?: string;
626
+ }
627
+ /**
628
+ * Routing factor with weight
629
+ */
630
+ interface RoutingFactor {
631
+ /** Factor name */
632
+ factor: string;
633
+ /** Weight (0-1) */
634
+ weight: number;
635
+ /** Human-readable description */
636
+ description: string;
637
+ }
638
+ /**
639
+ * Checkout configuration
640
+ */
641
+ interface Checkout {
642
+ /** Checkout method */
643
+ method: 'iframe' | 'redirect' | 'widget';
644
+ /** Checkout URL */
645
+ url: string;
646
+ /** URL expiry timestamp (ms) */
647
+ expiresAt?: number;
648
+ /** Allowed iframe features */
649
+ allowedFeatures?: string[];
650
+ }
651
+ /**
652
+ * Created checkout intent
653
+ */
654
+ interface CheckoutIntent {
655
+ /** Internal order ID */
656
+ orderId: string;
657
+ /** Gateway ID */
658
+ gateway: string;
659
+ /** Order status */
660
+ status: string;
661
+ /** Checkout configuration */
662
+ checkout: Checkout;
663
+ /** Gateway-specific widget config */
664
+ widgetConfig?: Record<string, unknown>;
665
+ }
666
+ /**
667
+ * Parameters for creating a checkout intent
668
+ */
669
+ interface CreateCheckoutIntentParams {
670
+ /** Gateway ID to use */
671
+ gateway: string;
672
+ /** External customer ID (optional — backend will auto-create guest if omitted) */
673
+ customerId?: string;
674
+ /** Fiat currency code */
675
+ fiatCurrency: string;
676
+ /** Fiat amount (required for buy, optional for sell) */
677
+ fiatAmount?: string;
678
+ /** Crypto amount (required for sell, optional for buy) */
679
+ cryptoAmount?: string;
680
+ /** Crypto currency code */
681
+ cryptoCurrency: string;
682
+ /** Network ID */
683
+ network: string;
684
+ /** Destination wallet address (required for buy, optional for sell) */
685
+ walletAddress?: string;
686
+ /** Order type: buy (fiat→crypto) or sell (crypto→fiat) */
687
+ orderType?: FlowType;
688
+ /** Payment method ID */
689
+ paymentMethodId?: string;
690
+ /** Customer email */
691
+ email?: string;
692
+ /** Redirect URL after completion */
693
+ redirectUrl?: string;
694
+ /** Custom metadata */
695
+ metadata?: Record<string, string>;
696
+ }
697
+ /**
698
+ * Normalized transaction status
699
+ */
700
+ type TransactionStatus = 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled' | 'expired' | 'refunded';
701
+ /**
702
+ * Transaction status details
703
+ */
704
+ interface Transaction {
705
+ /** Internal order ID */
706
+ orderId: string;
707
+ /** Gateway ID */
708
+ gateway: string;
709
+ /** Normalized status */
710
+ status: TransactionStatus;
711
+ /** Gateway's original status */
712
+ gatewayStatus: string;
713
+ /** Fiat amount */
714
+ fiatAmount: string;
715
+ /** Fiat currency */
716
+ fiatCurrency: string;
717
+ /** Crypto amount (if available) */
718
+ cryptoAmount?: string;
719
+ /** Crypto currency */
720
+ cryptoCurrency: string;
721
+ /** Network */
722
+ network: string;
723
+ /** Wallet address */
724
+ walletAddress: string;
725
+ /** Transaction hash (if available) */
726
+ transactionHash?: string;
727
+ /** Creation timestamp */
728
+ createdAt: string;
729
+ /** Completion timestamp (if completed) */
730
+ completedAt?: string;
731
+ }
419
732
 
420
733
  /**
421
734
  * SDK Validation Utilities
@@ -467,6 +780,15 @@ declare function validateRedirectUrl(url: string): ValidationResult;
467
780
  * @param chainType - Chain type from API (evm, solana, bitcoin, tezos, other)
468
781
  */
469
782
  declare function validateWalletAddress(address: string, chainType?: ChainType): ValidationResult;
783
+ /**
784
+ * Validate fiat amount against min/max limits
785
+ *
786
+ * @param amount - The fiat amount as a string
787
+ * @param minAmount - Minimum allowed amount
788
+ * @param maxAmount - Maximum allowed amount
789
+ * @param currency - Optional currency code to include in error messages
790
+ */
791
+ declare function validateAmount(amount: string, minAmount: number, maxAmount: number, currency?: string): ValidationResult;
470
792
  /**
471
793
  * Validate message origin against expected widget URL
472
794
  *
@@ -617,216 +939,82 @@ declare class RampApi {
617
939
  private getPublicCurrencies;
618
940
  /**
619
941
  * Validate a wallet address
620
- *
621
- * @param params - Address validation parameters
622
- * @returns Validation result with details
623
- *
624
- * @example
625
- * ```typescript
626
- * // Validate EVM address
627
- * const result = await api.validateAddress({
628
- * address: '0x742d35Cc6634C0532925a3b844Bc9e7595f1bD21',
629
- * network: 'ethereum',
630
- * });
631
- *
632
- * if (result.isValid) {
633
- * console.log('Normalized address:', result.normalizedAddress);
634
- * }
635
- *
636
- * // Auto-detect chain type
637
- * const autoResult = await api.validateAddress({
638
- * address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq',
639
- * });
640
- * console.log('Chain type:', autoResult.chainType); // 'bitcoin'
641
- * ```
642
942
  */
643
943
  validateAddress(params: ValidateAddressParams): Promise<AddressValidationResult>;
644
944
  /**
645
945
  * Validate multiple wallet addresses in batch
646
- *
647
- * @param addresses - Array of address validation parameters (max 20)
648
- * @returns Batch validation results
649
- *
650
- * @example
651
- * ```typescript
652
- * const results = await api.validateAddressBatch([
653
- * { address: '0x742d35Cc6634C0532925a3b844Bc9e7595f1bD21', network: 'ethereum' },
654
- * { address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', chainType: 'bitcoin' },
655
- * { address: 'tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb' },
656
- * ]);
657
- *
658
- * console.log(`Valid: ${results.validCount}, Invalid: ${results.invalidCount}`);
659
- * ```
660
946
  */
661
947
  validateAddressBatch(addresses: ValidateAddressParams[]): Promise<AddressValidationBatchResult>;
662
948
  /**
663
949
  * Get list of supported networks for address validation
664
- *
665
- * @returns Supported networks with chain type information
666
- *
667
- * @example
668
- * ```typescript
669
- * const networks = await api.getSupportedNetworks();
670
- *
671
- * console.log('EVM networks:', networks.grouped.evm.map(n => n.name));
672
- * console.log('Total networks:', networks.total);
673
- * ```
674
950
  */
675
951
  getSupportedNetworks(): Promise<SupportedNetworksResult>;
676
952
  /**
677
953
  * Create a session for secure widget initialization
678
- *
679
- * Sessions follow the Stripe security model:
680
- * 1. Partner backend creates session with secret key (sk_)
681
- * 2. Widget is initialized with sessionId + clientSecret
682
- * 3. Public key alone cannot create customers, orders, or initiate KYC
683
- *
684
- * @param params - Session parameters
685
- * @returns Session with client secret (only returned once!)
686
- *
687
- * @example
688
- * ```typescript
689
- * // On your backend (using secret key)
690
- * const session = await api.createSession({
691
- * externalCustomerId: 'user_123',
692
- * sourceAmount: 100,
693
- * sourceCurrency: 'USD',
694
- * destinationCurrency: 'ETH',
695
- * network: 'ethereum',
696
- * redirectUrl: 'https://yourapp.com/success',
697
- * });
698
- *
699
- * // Return sessionId and clientSecret to frontend
700
- * return { sessionId: session.sessionId, clientSecret: session.clientSecret };
701
- *
702
- * // On your frontend (using public key)
703
- * const widget = new RampWidget({
704
- * apiKey: 'pk_xxx',
705
- * projectId: 'proj_xxx',
706
- * sessionId: session.sessionId,
707
- * clientSecret: session.clientSecret,
708
- * });
709
- * ```
710
954
  */
711
955
  createSession(params: CreateSessionParams): Promise<SessionWithSecret>;
712
956
  /**
713
957
  * Get session by ID
714
- *
715
- * @param sessionId - Session ID (sess_xxx)
716
- * @returns Session data
717
- *
718
- * @example
719
- * ```typescript
720
- * const session = await api.getSession('sess_abc123');
721
- * console.log('Status:', session.status);
722
- * ```
723
958
  */
724
959
  getSession(sessionId: string): Promise<Session>;
725
960
  /**
726
961
  * Cancel a session
727
- *
728
- * Can only cancel sessions in 'pending' or 'confirmed' status.
729
- * Requires secret key (sk_).
730
- *
731
- * @param sessionId - Session ID to cancel
732
- * @returns Updated session data
733
- *
734
- * @example
735
- * ```typescript
736
- * const session = await api.cancelSession('sess_abc123');
737
- * console.log('Status:', session.status); // 'cancelled'
738
- * ```
739
962
  */
740
963
  cancelSession(sessionId: string): Promise<Session>;
964
+ /**
965
+ * Get supported configuration
966
+ * Returns available gateways, currencies, and payment methods
967
+ */
968
+ getSupported(orderType?: 'buy' | 'sell'): Promise<OnrampSupported>;
969
+ /**
970
+ * Get quotes from all gateways
971
+ */
972
+ getQuotes(params: GetQuotesParams): Promise<QuotesResponse>;
973
+ /**
974
+ * Create a checkout intent with a specific gateway
975
+ */
976
+ createCheckoutIntent(params: CreateCheckoutIntentParams): Promise<CheckoutIntent>;
977
+ /**
978
+ * Get transaction status
979
+ */
980
+ getTransaction(transactionId: string): Promise<Transaction>;
741
981
  }
742
982
  /**
743
983
  * Standalone function to get conversion rate
744
- * Convenience wrapper for one-off rate checks
745
- *
746
- * @example
747
- * ```typescript
748
- * import { getRate } from '@nowramp/sdk';
749
- *
750
- * const rate = await getRate(
751
- * { apiKey: 'ramp_pk_xxx', projectId: 'proj_xxx' },
752
- * { from: 'USD', to: 'ETH', amount: 100 }
753
- * );
754
- * ```
755
984
  */
756
985
  declare function getRate(config: RampApiConfig, params: GetRateParams): Promise<RateResult>;
757
986
  /**
758
987
  * Standalone function to get available currencies
759
- * Convenience wrapper for one-off currency fetching
760
- *
761
- * @example
762
- * ```typescript
763
- * import { getCurrencies } from '@nowramp/sdk';
764
- *
765
- * const currencies = await getCurrencies({
766
- * apiKey: 'ramp_pk_xxx',
767
- * projectId: 'proj_xxx'
768
- * });
769
- * ```
770
988
  */
771
989
  declare function getCurrencies(config: RampApiConfig): Promise<AvailableCurrencies>;
772
990
  /**
773
991
  * Standalone function to validate a wallet address
774
- * Convenience wrapper for one-off address validation
775
- *
776
- * Note: This is a public endpoint that doesn't require authentication
777
- *
778
- * @example
779
- * ```typescript
780
- * import { validateAddress } from '@nowramp/sdk';
781
- *
782
- * const result = await validateAddress(
783
- * { apiUrl: 'https://api.nowramp.com' },
784
- * { address: '0x742d35Cc6634C0532925a3b844Bc9e7595f1bD21', network: 'ethereum' }
785
- * );
786
- *
787
- * if (result.isValid) {
788
- * console.log('Address is valid!', result.normalizedAddress);
789
- * }
790
- * ```
791
992
  */
792
993
  declare function validateAddress(config: Pick<RampApiConfig, 'apiUrl'>, params: ValidateAddressParams): Promise<AddressValidationResult>;
793
994
  /**
794
995
  * Standalone function to validate multiple wallet addresses
795
- * Convenience wrapper for one-off batch validation
796
- *
797
- * Note: This is a public endpoint that doesn't require authentication
798
- *
799
- * @example
800
- * ```typescript
801
- * import { validateAddressBatch } from '@nowramp/sdk';
802
- *
803
- * const results = await validateAddressBatch(
804
- * { apiUrl: 'https://api.nowramp.com' },
805
- * [
806
- * { address: '0x742d35Cc6634C0532925a3b844Bc9e7595f1bD21', network: 'ethereum' },
807
- * { address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', chainType: 'bitcoin' },
808
- * ]
809
- * );
810
- *
811
- * console.log(`Valid: ${results.validCount}, Invalid: ${results.invalidCount}`);
812
- * ```
813
996
  */
814
997
  declare function validateAddressBatch(config: Pick<RampApiConfig, 'apiUrl'>, addresses: ValidateAddressParams[]): Promise<AddressValidationBatchResult>;
815
998
  /**
816
999
  * Standalone function to get supported networks for validation
817
- * Convenience wrapper for one-off network listing
818
- *
819
- * Note: This is a public endpoint that doesn't require authentication
820
- *
821
- * @example
822
- * ```typescript
823
- * import { getSupportedNetworks } from '@nowramp/sdk';
824
- *
825
- * const networks = await getSupportedNetworks({ apiUrl: 'https://api.nowramp.com' });
826
- * console.log('EVM networks:', networks.grouped.evm.map(n => n.name));
827
- * ```
828
1000
  */
829
1001
  declare function getSupportedNetworks(config: Pick<RampApiConfig, 'apiUrl'>): Promise<SupportedNetworksResult>;
1002
+ /**
1003
+ * Standalone function to get supported configuration
1004
+ */
1005
+ declare function getSupported(config: Pick<RampApiConfig, 'apiUrl' | 'projectId'>, orderType?: 'buy' | 'sell'): Promise<OnrampSupported>;
1006
+ /**
1007
+ * Standalone function to get quotes from all gateways
1008
+ */
1009
+ declare function getQuotes(config: Pick<RampApiConfig, 'apiUrl' | 'projectId'>, params: GetQuotesParams): Promise<QuotesResponse>;
1010
+ /**
1011
+ * Standalone function to create a checkout intent
1012
+ */
1013
+ declare function createCheckoutIntent(config: Pick<RampApiConfig, 'apiUrl' | 'projectId'>, params: CreateCheckoutIntentParams): Promise<CheckoutIntent>;
1014
+ /**
1015
+ * Standalone function to get transaction status
1016
+ */
1017
+ declare function getTransaction(config: Pick<RampApiConfig, 'apiUrl'>, transactionId: string): Promise<Transaction>;
830
1018
 
831
1019
  /**
832
1020
  * NowRamp Widget SDK
@@ -853,5 +1041,5 @@ declare function getSupportedNetworks(config: Pick<RampApiConfig, 'apiUrl'>): Pr
853
1041
  */
854
1042
  declare const VERSION: string;
855
1043
 
856
- export { ADDRESS_PATTERNS, ALLOWED_PROTOCOLS, API_KEY_PREFIXES, DEV_ALLOWED_HOSTNAMES, RampApi, RampWidget, VERSION, RampWidget as default, getCurrencies, getRate, getSupportedNetworks, validateAddress, validateAddressBatch, validateApiKey, validateMessageOrigin, validateRedirectUrl, validateWalletAddress };
857
- export type { AddressValidationBatchResult, AddressValidationResult, AvailableCurrencies, ChainType, CreateSessionParams, CryptoCurrency, ErrorData, FiatCurrency, FlowType, GetRateParams, Locale, OrderData, QuoteData, RampApiConfig, RampWidgetConfig, RateResult, Session, SessionStatus, SessionType, SessionWithSecret, SupportedNetwork, SupportedNetworksResult, Theme, ValidateAddressParams, ValidationChainType, ValidationDetails, ValidationResult, WidgetEventType, WidgetMessage };
1044
+ export { ADDRESS_PATTERNS, ALLOWED_PROTOCOLS, API_KEY_PREFIXES, DEV_ALLOWED_HOSTNAMES, RampApi, RampWidget, VERSION, createCheckoutIntent, RampWidget as default, getCurrencies, getQuotes, getRate, getSupported, getSupportedNetworks, getTransaction, validateAddress, validateAddressBatch, validateAmount, validateApiKey, validateMessageOrigin, validateRedirectUrl, validateWalletAddress };
1045
+ export type { AddressValidationBatchResult, AddressValidationResult, AvailableCurrencies, ChainType, Checkout, CheckoutIntent, CreateCheckoutIntentParams, CreateSessionParams, CryptoCurrency, ErrorData, FiatCurrency, FieldLockConfig, FieldLocksConfig, FlowType, Gateway, GatewayFeatures, GetQuotesParams, GetRateParams, Locale, OnrampSupported, OrderData, Quote, QuoteData, QuoteFees, QuotesResponse, RampApiConfig, RampWidgetConfig, RateResult, RoutingFactor, Session, SessionStatus, SessionType, SessionWithSecret, SupportedCrypto, SupportedFiat, SupportedNetwork, SupportedNetworkInfo, SupportedNetworksResult, SupportedPaymentMethod, Theme, Transaction, TransactionStatus, UnavailableGateway, ValidateAddressParams, ValidationChainType, ValidationDetails, ValidationResult, WidgetEventType, WidgetMessage };
package/dist/index.js CHANGED
@@ -3,4 +3,4 @@
3
3
  * (c) 2026 NowRamp
4
4
  * @license MIT
5
5
  */
6
- const e={evm:{pattern:/^0x[a-fA-F0-9]{40}$/,description:"EVM address must start with 0x followed by 40 hexadecimal characters"},solana:{pattern:/^[1-9A-HJ-NP-Za-km-z]{32,44}$/,description:"Solana address must be 32-44 base58 characters"},bitcoin:{pattern:/^(1[a-km-zA-HJ-NP-Z1-9]{25,34}|3[a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})$/,description:"Bitcoin address must be a valid legacy, SegWit, or native SegWit format"},tezos:{pattern:/^(tz[123]|KT1)[a-zA-Z0-9]{33}$/,description:"Tezos address must start with tz1/tz2/tz3 (implicit) or KT1 (contract)"},other:{pattern:/^.{10,100}$/,description:"Address format varies by network (10-100 characters)"}},t=["https:"],r=["localhost","127.0.0.1"],s={public:"pk_",secret:"sk_"};function i(e){return e?e.includes(s.secret)?{valid:!1,error:"Secret API keys (sk_) cannot be used in the SDK. Use a public key (pk_) instead. Secret keys should only be used in backend server code."}:(e.includes(s.public)||console.warn("RampWidget: API key does not have a public key prefix (pk_). For security, please generate a new public key for frontend use."),{valid:!0}):{valid:!1,error:"apiKey is required"}}function n(e){if(!e)return{valid:!0};try{const s=new URL(e);return t.includes(s.protocol)||"http:"===s.protocol&&r.includes(s.hostname)?{valid:!0}:{valid:!1,error:`redirectUrl must use HTTPS (got ${s.protocol})`}}catch{return{valid:!1,error:"redirectUrl is not a valid URL"}}}function o(t,r){if(!t)return{valid:!0};const s=t.trim();if(r){const t=r.toLowerCase(),i=e[t];if(i){const{pattern:e,description:t}=i;return e.test(s)?{valid:!0}:{valid:!1,error:t}}return s.length>=10&&s.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}return s.length>=10&&s.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}function a(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const r=new URL(e);return"localhost"===r.hostname||"127.0.0.1"===r.hostname}catch{return!1}}class c{constructor(e){this.iframe=null,this.modalContainer=null,this.boundMessageHandler=null,this.isOpen=!1,this.config={widgetUrl:e.widgetUrl||"https://widget.nowramp.com/widget/latest/index.html",apiUrl:e.apiUrl||"https://api.nowramp.com",flowType:"buy",theme:"light",...e},this.validateConfig()}open(){this.isOpen?console.warn("RampWidget: Widget is already open"):(this.isOpen=!0,this.setupMessageListener(),this.createIframe())}close(){this.isOpen&&(this.isOpen=!1,this.cleanup(),this.config.onClose?.())}setAmount(e){this.sendCommand("SET_AMOUNT",e)}setTheme(e){this.sendCommand("SET_THEME",e)}refreshQuote(){this.sendCommand("REFRESH_QUOTE")}validateConfig(){if(!this.config.apiKey)throw new Error("RampWidget: apiKey is required");if(!this.config.projectId)throw new Error("RampWidget: projectId is required");if(!!(!this.config.sessionId||!this.config.clientSecret)&&!this.config.externalUserId)throw new Error("RampWidget: Either externalUserId (legacy) or sessionId + clientSecret (recommended) is required");if(this.config.sessionId&&!this.config.clientSecret)throw new Error("RampWidget: clientSecret is required when using sessionId");const e=i(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=n(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=o(this.config.walletAddress,void 0);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return this.config.sessionId&&this.config.clientSecret&&(e.set("sessionId",this.config.sessionId),e.set("clientSecret",this.config.clientSecret)),this.config.externalUserId&&e.set("externalUserId",this.config.externalUserId),this.config.apiUrl&&e.set("apiUrl",this.config.apiUrl),this.config.locale&&e.set("locale",this.config.locale),void 0!==this.config.amount&&e.set("amount",String(this.config.amount)),this.config.sourceCurrency&&e.set("sourceCurrency",this.config.sourceCurrency),this.config.destinationCurrency&&e.set("destinationCurrency",this.config.destinationCurrency),this.config.walletAddress&&e.set("walletAddress",this.config.walletAddress),this.config.network&&e.set("network",this.config.network),this.config.redirectUrl&&e.set("redirectUrl",this.config.redirectUrl),!1===this.config.autoRedirect&&e.set("autoRedirect","false"),`${this.config.widgetUrl}?${e.toString()}`}createIframe(){this.iframe=document.createElement("iframe"),this.iframe.src=this.buildWidgetUrl(),this.iframe.style.border="none",this.iframe.style.width="100%",this.iframe.style.height="100%",this.iframe.allow="camera *; microphone *; geolocation *; encrypted-media *; clipboard-read; clipboard-write; payment *",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-popups-without-user-activation allow-top-navigation allow-downloads");const e=this.getContainer();e?e.appendChild(this.iframe):this.createModal()}getContainer(){return this.config.container?"string"==typeof this.config.container?document.querySelector(this.config.container):this.config.container:null}createModal(){this.modalContainer=document.createElement("div"),this.modalContainer.style.cssText="\n position: fixed;\n inset: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n ";const e="dark"===this.config.theme,t=document.createElement("div");t.style.cssText=`\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: ${e?"#000000":"white"};\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n `,this.iframe&&t.appendChild(this.iframe),this.modalContainer.appendChild(t),this.modalContainer.onclick=e=>{e.target===this.modalContainer&&this.close()};document.addEventListener("keydown",e=>{"Escape"===e.key&&this.close()}),document.body.appendChild(this.modalContainer),document.body.style.overflow="hidden"}setupMessageListener(){this.boundMessageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.boundMessageHandler)}handleMessage(e){if(!a(e.origin,this.config.widgetUrl))return;const t=e.data;if(t&&"ramp-widget"===t.source)switch(t.type){case"WIDGET_READY":this.config.onReady?.();break;case"WIDGET_CLOSE":this.close();break;case"STEP_CHANGE":this.config.onStepChange?.(t.payload.step);break;case"QUOTE_CREATED":this.config.onQuoteCreated?.(t.payload);break;case"ORDER_CREATED":this.config.onOrderCreated?.(t.payload);break;case"ORDER_COMPLETED":this.config.onSuccess?.(t.payload);break;case"ERROR":this.config.onError?.(t.payload)}}sendCommand(e,t){if(!this.iframe?.contentWindow)return void console.warn("RampWidget: Cannot send command, widget not open");const r=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},r)}cleanup(){this.boundMessageHandler&&(window.removeEventListener("message",this.boundMessageHandler),this.boundMessageHandler=null),this.iframe?.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.modalContainer?.parentNode&&(this.modalContainer.parentNode.removeChild(this.modalContainer),document.body.style.overflow=""),this.modalContainer=null}}class d{constructor(e){this.config=e,this.baseUrl=e.apiUrl||"https://api.nowramp.com"}async getRate(e){if(!this.config.apiKey)return this.getPublicRate(e);const t=await fetch(`${this.baseUrl}/v1/rates/convert`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{rate:s.rate,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationAmount:s.destinationAmount,destinationCurrency:s.destinationCurrency,fees:{processingFee:s.fees?.processingFee||0,processingFeePercent:s.fees?.processingFeePercent||0,networkFee:s.fees?.networkFee||0,totalFee:s.fees?.totalFee||0},provider:s.provider||"unknown",expiresAt:s.expiresAt}}async getPublicRate(e){const t=await fetch(`${this.baseUrl}/public/wert/convert`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{rate:s.ticker||s.rate,sourceAmount:s.currencyAmount||s.sourceAmount||e.amount,sourceCurrency:e.from,destinationAmount:s.commodityAmount||s.destinationAmount,destinationCurrency:e.to,fees:{processingFee:s.feeAmount||s.fees?.processingFee||0,processingFeePercent:s.feePercent||s.fees?.processingFeePercent||0,networkFee:s.currencyMinerFee||s.fees?.networkFee||0,totalFee:(s.feeAmount||0)+(s.currencyMinerFee||0)||s.fees?.totalFee||0},provider:"wert",expiresAt:s.expiresAt}}async getCurrencies(){if(!this.config.apiKey)return this.getPublicCurrencies();const e=await fetch(`${this.baseUrl}/v1/rates/currencies`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${r}`)}const t=await e.json(),r=t.data?.attributes||t.data||t;return{fiat:r.fiat||[],crypto:r.crypto||[],provider:r.provider||"unknown",environment:r.environment||"sandbox"}}async getPublicCurrencies(){if(!this.config.projectId)throw new Error("projectId is required to fetch currencies without an API key");const e=await fetch(`${this.baseUrl}/public/projects/${this.config.projectId}/currencies`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${r}`)}const t=await e.json(),r=t.data?.attributes||t.data||t;return{fiat:r.fiat||[],crypto:r.crypto||[],provider:r.provider||"unknown",environment:r.environment||"sandbox"}}async validateAddress(e){const t=await fetch(`${this.baseUrl}/public/validate/address`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate address: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async validateAddressBatch(e){if(e.length>20)throw new Error("Maximum 20 addresses per batch");const t=await fetch(`${this.baseUrl}/public/validate/address/batch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({addresses:e})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate addresses: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async getSupportedNetworks(){const e=await fetch(`${this.baseUrl}/public/validate/networks`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get supported networks: ${r}`)}const t=await e.json();return t.data?.attributes||t.data||t}async createSession(e){if(!this.config.apiKey)throw new Error("API key is required to create sessions. Use a secret key (sk_) for this operation.");const t=await fetch(`${this.baseUrl}/v1/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify(e)});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to create session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,clientSecret:s.clientSecret,type:s.type||"onramp",status:s.status||"pending",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,expiresAt:s.expiresAt,createdAt:s.createdAt}}async getSession(e){if(!this.config.apiKey)throw new Error("API key is required to get session details.");const t=await fetch(`${this.baseUrl}/v1/sessions/${e}`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,type:s.type||"onramp",status:s.status||"pending",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,fieldLocks:s.fieldLocks,orderId:s.orderId,expiresAt:s.expiresAt,confirmedAt:s.confirmedAt,completedAt:s.completedAt,createdAt:s.createdAt}}async cancelSession(e){if(!this.config.apiKey)throw new Error("API key is required to cancel sessions. Use a secret key (sk_) for this operation.");const t=await fetch(`${this.baseUrl}/v1/sessions/${e}/cancel`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to cancel session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,type:s.type||"onramp",status:s.status||"cancelled",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,orderId:s.orderId,expiresAt:s.expiresAt,confirmedAt:s.confirmedAt,completedAt:s.completedAt,createdAt:s.createdAt}}}async function l(e,t){return new d(e).getRate(t)}async function h(e){return new d(e).getCurrencies()}async function u(e,t){return new d({apiKey:"",projectId:"",...e}).validateAddress(t)}async function p(e,t){return new d({apiKey:"",projectId:"",...e}).validateAddressBatch(t)}async function m(e){return new d({apiKey:"",projectId:"",...e}).getSupportedNetworks()}const f="0.1.13";export{e as ADDRESS_PATTERNS,t as ALLOWED_PROTOCOLS,s as API_KEY_PREFIXES,r as DEV_ALLOWED_HOSTNAMES,d as RampApi,c as RampWidget,f as VERSION,c as default,h as getCurrencies,l as getRate,m as getSupportedNetworks,u as validateAddress,p as validateAddressBatch,i as validateApiKey,a as validateMessageOrigin,n as validateRedirectUrl,o as validateWalletAddress};
6
+ const e={evm:{pattern:/^0x[a-fA-F0-9]{40}$/,description:"EVM address must start with 0x followed by 40 hexadecimal characters"},solana:{pattern:/^[1-9A-HJ-NP-Za-km-z]{32,44}$/,description:"Solana address must be 32-44 base58 characters"},bitcoin:{pattern:/^(1[a-km-zA-HJ-NP-Z1-9]{25,34}|3[a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})$/,description:"Bitcoin address must be a valid legacy, SegWit, or native SegWit format"},tezos:{pattern:/^(tz[123]|KT1)[a-zA-Z0-9]{33}$/,description:"Tezos address must start with tz1/tz2/tz3 (implicit) or KT1 (contract)"},other:{pattern:/^.{10,100}$/,description:"Address format varies by network (10-100 characters)"}},t=["https:"],r=["localhost","127.0.0.1"],s={public:"pk_",secret:"sk_"};function o(e){return e?e.includes(s.secret)?{valid:!1,error:"Secret API keys (sk_) cannot be used in the SDK. Use a public key (pk_) instead. Secret keys should only be used in backend server code."}:(e.includes(s.public)||console.warn("RampWidget: API key does not have a public key prefix (pk_). For security, please generate a new public key for frontend use."),{valid:!0}):{valid:!1,error:"apiKey is required"}}function n(e){if(!e)return{valid:!0};try{const s=new URL(e);return t.includes(s.protocol)||"http:"===s.protocol&&r.includes(s.hostname)?{valid:!0}:{valid:!1,error:`redirectUrl must use HTTPS (got ${s.protocol})`}}catch{return{valid:!1,error:"redirectUrl is not a valid URL"}}}function i(t,r){if(!t)return{valid:!0};const s=t.trim();if(r){const t=r.toLowerCase(),o=e[t];if(o){const{pattern:e,description:t}=o;return e.test(s)?{valid:!0}:{valid:!1,error:t}}return s.length>=10&&s.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}return s.length>=10&&s.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}function a(e,t,r,s){if(!e||""===e.trim())return{valid:!1,error:"Amount is required"};const o=parseFloat(e);if(isNaN(o))return{valid:!1,error:"Amount must be a valid number"};if(o<=0)return{valid:!1,error:"Amount must be greater than zero"};const n=s?` ${s}`:"";return o<t?{valid:!1,error:`Minimum amount is ${t}${n}`}:o>r?{valid:!1,error:`Maximum amount is ${r}${n}`}:{valid:!0}}function c(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const r=new URL(e);return"localhost"===r.hostname||"127.0.0.1"===r.hostname}catch{return!1}}class d{constructor(e){this.iframe=null,this.modalContainer=null,this.boundMessageHandler=null,this.isOpen=!1,this.config={widgetUrl:e.widgetUrl||"https://widget.nowramp.com/widget/latest/index.html",apiUrl:e.apiUrl||"https://api.nowramp.com",flowType:"buy",theme:"light",...e},this.validateConfig()}open(){this.isOpen?console.warn("RampWidget: Widget is already open"):(this.isOpen=!0,this.setupMessageListener(),this.createIframe())}close(){this.isOpen&&(this.isOpen=!1,this.cleanup(),this.config.onClose?.())}setAmount(e){this.sendCommand("SET_AMOUNT",e)}setTheme(e){this.sendCommand("SET_THEME",e)}refreshQuote(){this.sendCommand("REFRESH_QUOTE")}validateConfig(){if(!this.config.apiKey)throw new Error("RampWidget: apiKey is required");if(!this.config.projectId)throw new Error("RampWidget: projectId is required");if(!!(!this.config.sessionId||!this.config.clientSecret)&&!this.config.externalUserId)throw new Error("RampWidget: Either externalUserId (legacy) or sessionId + clientSecret (recommended) is required");if(this.config.sessionId&&!this.config.clientSecret)throw new Error("RampWidget: clientSecret is required when using sessionId");const e=o(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=n(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=i(this.config.walletAddress,void 0);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return this.config.sessionId&&this.config.clientSecret&&(e.set("sessionId",this.config.sessionId),e.set("clientSecret",this.config.clientSecret)),this.config.externalUserId&&e.set("externalUserId",this.config.externalUserId),this.config.apiUrl&&e.set("apiUrl",this.config.apiUrl),this.config.locale&&e.set("locale",this.config.locale),void 0!==this.config.amount&&e.set("amount",String(this.config.amount)),this.config.sourceCurrency&&e.set("sourceCurrency",this.config.sourceCurrency),this.config.destinationCurrency&&e.set("destinationCurrency",this.config.destinationCurrency),this.config.walletAddress&&e.set("walletAddress",this.config.walletAddress),this.config.network&&e.set("network",this.config.network),this.config.redirectUrl&&e.set("redirectUrl",this.config.redirectUrl),!1===this.config.autoRedirect&&e.set("autoRedirect","false"),`${this.config.widgetUrl}?${e.toString()}`}createIframe(){this.iframe=document.createElement("iframe"),this.iframe.src=this.buildWidgetUrl(),this.iframe.style.border="none",this.iframe.style.width="100%",this.iframe.style.height="100%",this.iframe.allow="camera *; microphone *; geolocation *; encrypted-media *; clipboard-read; clipboard-write; payment *",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-popups-without-user-activation allow-top-navigation allow-downloads");const e=this.getContainer();e?e.appendChild(this.iframe):this.createModal()}getContainer(){return this.config.container?"string"==typeof this.config.container?document.querySelector(this.config.container):this.config.container:null}createModal(){this.modalContainer=document.createElement("div"),this.modalContainer.style.cssText="\n position: fixed;\n inset: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n ";const e="dark"===this.config.theme,t=document.createElement("div");t.style.cssText=`\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: ${e?"#000000":"white"};\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n `,this.iframe&&t.appendChild(this.iframe),this.modalContainer.appendChild(t),this.modalContainer.onclick=e=>{e.target===this.modalContainer&&this.close()};document.addEventListener("keydown",e=>{"Escape"===e.key&&this.close()}),document.body.appendChild(this.modalContainer),document.body.style.overflow="hidden"}setupMessageListener(){this.boundMessageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.boundMessageHandler)}handleMessage(e){if(!c(e.origin,this.config.widgetUrl))return;const t=e.data;if(t&&"ramp-widget"===t.source)switch(t.type){case"WIDGET_READY":this.config.onReady?.();break;case"WIDGET_CLOSE":this.close();break;case"STEP_CHANGE":this.config.onStepChange?.(t.payload.step);break;case"QUOTE_CREATED":this.config.onQuoteCreated?.(t.payload);break;case"ORDER_CREATED":this.config.onOrderCreated?.(t.payload);break;case"ORDER_COMPLETED":this.config.onSuccess?.(t.payload);break;case"ERROR":this.config.onError?.(t.payload)}}sendCommand(e,t){if(!this.iframe?.contentWindow)return void console.warn("RampWidget: Cannot send command, widget not open");const r=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},r)}cleanup(){this.boundMessageHandler&&(window.removeEventListener("message",this.boundMessageHandler),this.boundMessageHandler=null),this.iframe?.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.modalContainer?.parentNode&&(this.modalContainer.parentNode.removeChild(this.modalContainer),document.body.style.overflow=""),this.modalContainer=null}}class l{constructor(e){this.config=e,this.baseUrl=e.apiUrl||"https://api.nowramp.com"}async getRate(e){if(!this.config.apiKey)return this.getPublicRate(e);const t=await fetch(`${this.baseUrl}/v1/rates/convert`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{rate:s.rate,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationAmount:s.destinationAmount,destinationCurrency:s.destinationCurrency,fees:{processingFee:s.fees?.processingFee||0,processingFeePercent:s.fees?.processingFeePercent||0,networkFee:s.fees?.networkFee||0,totalFee:s.fees?.totalFee||0},provider:s.provider||"unknown",expiresAt:s.expiresAt}}async getPublicRate(e){const t=await fetch(`${this.baseUrl}/public/wert/convert`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{rate:s.ticker||s.rate,sourceAmount:s.currencyAmount||s.sourceAmount||e.amount,sourceCurrency:e.from,destinationAmount:s.commodityAmount||s.destinationAmount,destinationCurrency:e.to,fees:{processingFee:s.feeAmount||s.fees?.processingFee||0,processingFeePercent:s.feePercent||s.fees?.processingFeePercent||0,networkFee:s.currencyMinerFee||s.fees?.networkFee||0,totalFee:(s.feeAmount||0)+(s.currencyMinerFee||0)||s.fees?.totalFee||0},provider:"wert",expiresAt:s.expiresAt}}async getCurrencies(){if(!this.config.apiKey)return this.getPublicCurrencies();const e=await fetch(`${this.baseUrl}/v1/rates/currencies`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${r}`)}const t=await e.json(),r=t.data?.attributes||t.data||t;return{fiat:r.fiat||[],crypto:r.crypto||[],provider:r.provider||"unknown",environment:r.environment||"sandbox"}}async getPublicCurrencies(){if(!this.config.projectId)throw new Error("projectId is required to fetch currencies without an API key");const e=await fetch(`${this.baseUrl}/public/projects/${this.config.projectId}/currencies`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${r}`)}const t=await e.json(),r=t.data?.attributes||t.data||t;return{fiat:r.fiat||[],crypto:r.crypto||[],provider:r.provider||"unknown",environment:r.environment||"sandbox"}}async validateAddress(e){const t=await fetch(`${this.baseUrl}/public/validate/address`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate address: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async validateAddressBatch(e){if(e.length>20)throw new Error("Maximum 20 addresses per batch");const t=await fetch(`${this.baseUrl}/public/validate/address/batch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({addresses:e})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate addresses: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async getSupportedNetworks(){const e=await fetch(`${this.baseUrl}/public/validate/networks`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get supported networks: ${r}`)}const t=await e.json();return t.data?.attributes||t.data||t}async createSession(e){if(!this.config.apiKey)throw new Error("API key is required to create sessions. Use a secret key (sk_) for this operation.");const t=await fetch(`${this.baseUrl}/v1/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify(e)});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to create session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,clientSecret:s.clientSecret,type:s.type||"onramp",status:s.status||"pending",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,expiresAt:s.expiresAt,createdAt:s.createdAt}}async getSession(e){if(!this.config.apiKey)throw new Error("API key is required to get session details.");const t=await fetch(`${this.baseUrl}/v1/sessions/${e}`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,type:s.type||"onramp",status:s.status||"pending",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,fieldLocks:s.fieldLocks,orderId:s.orderId,expiresAt:s.expiresAt,confirmedAt:s.confirmedAt,completedAt:s.completedAt,createdAt:s.createdAt}}async cancelSession(e){if(!this.config.apiKey)throw new Error("API key is required to cancel sessions. Use a secret key (sk_) for this operation.");const t=await fetch(`${this.baseUrl}/v1/sessions/${e}/cancel`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to cancel session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,type:s.type||"onramp",status:s.status||"cancelled",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,orderId:s.orderId,expiresAt:s.expiresAt,confirmedAt:s.confirmedAt,completedAt:s.completedAt,createdAt:s.createdAt}}async getSupported(e){if(!this.config.projectId)throw new Error("projectId is required to get supported config");const t=new URLSearchParams({projectId:this.config.projectId});e&&t.set("orderType",e);const r=await fetch(`${this.baseUrl}/public/v1/onramp/supported?${t}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!r.ok){const e=await r.json().catch(()=>({})),t=e.error?.message||e.message||`HTTP ${r.status}`;throw new Error(`Failed to get supported config: ${t}`)}const s=await r.json();return s.data?.attributes||s.data||s}async getQuotes(e){if(!this.config.projectId)throw new Error("projectId is required to get quotes");const t=new URLSearchParams({projectId:this.config.projectId,fiatCurrency:e.fiatCurrency,cryptoCurrency:e.cryptoCurrency,network:e.network});e.fiatAmount&&t.set("fiatAmount",e.fiatAmount),e.cryptoAmount&&t.set("cryptoAmount",e.cryptoAmount),e.orderType&&t.set("orderType",e.orderType),e.paymentMethodId&&t.set("paymentMethodId",e.paymentMethodId),e.country&&t.set("country",e.country);const r=await fetch(`${this.baseUrl}/public/v1/onramp/quotes?${t}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!r.ok){const e=await r.json().catch(()=>({})),t=e.error?.message||e.message||`HTTP ${r.status}`;throw new Error(`Failed to get quotes: ${t}`)}const s=await r.json();return s.data?.attributes||s.data||s}async createCheckoutIntent(e){if(!this.config.projectId)throw new Error("projectId is required to create checkout intents");const t=await fetch(`${this.baseUrl}/public/v1/onramp/checkout-intent`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectId:this.config.projectId,...e})});if(!t.ok){const e=await t.json().catch(()=>({})),r=("string"==typeof e.error?e.error:e.error?.message)||e.message||`HTTP ${t.status}`;let s="";if(e.details)try{const t="string"==typeof e.details?JSON.parse(e.details):e.details;if(t.errors&&"object"==typeof t.errors){const e=Object.values(t.errors).flat().filter(Boolean);e.length&&(s=e.join("; "))}}catch{}throw new Error(`Failed to create checkout intent: ${s||r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async getTransaction(e){const t=await fetch(`${this.baseUrl}/public/v1/onramp/transactions/${e}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get transaction: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}}async function u(e,t){return new l(e).getRate(t)}async function h(e){return new l(e).getCurrencies()}async function p(e,t){return new l({apiKey:"",projectId:"",...e}).validateAddress(t)}async function m(e,t){return new l({apiKey:"",projectId:"",...e}).validateAddressBatch(t)}async function f(e){return new l({apiKey:"",projectId:"",...e}).getSupportedNetworks()}async function g(e,t){return new l({apiKey:"",...e}).getSupported(t)}async function w(e,t){return new l({apiKey:"",...e}).getQuotes(t)}async function y(e,t){return new l({apiKey:"",...e}).createCheckoutIntent(t)}async function b(e,t){return new l({apiKey:"",projectId:"",...e}).getTransaction(t)}const A="0.1.13";export{e as ADDRESS_PATTERNS,t as ALLOWED_PROTOCOLS,s as API_KEY_PREFIXES,r as DEV_ALLOWED_HOSTNAMES,l as RampApi,d as RampWidget,A as VERSION,y as createCheckoutIntent,d as default,h as getCurrencies,w as getQuotes,u as getRate,g as getSupported,f as getSupportedNetworks,b as getTransaction,p as validateAddress,m as validateAddressBatch,a as validateAmount,o as validateApiKey,c as validateMessageOrigin,n as validateRedirectUrl,i as validateWalletAddress};
package/dist/sdk.umd.js CHANGED
@@ -3,4 +3,4 @@
3
3
  * (c) 2026 NowRamp
4
4
  * @license MIT
5
5
  */
6
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).NowRamp={})}(this,function(e){"use strict";const t={evm:{pattern:/^0x[a-fA-F0-9]{40}$/,description:"EVM address must start with 0x followed by 40 hexadecimal characters"},solana:{pattern:/^[1-9A-HJ-NP-Za-km-z]{32,44}$/,description:"Solana address must be 32-44 base58 characters"},bitcoin:{pattern:/^(1[a-km-zA-HJ-NP-Z1-9]{25,34}|3[a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})$/,description:"Bitcoin address must be a valid legacy, SegWit, or native SegWit format"},tezos:{pattern:/^(tz[123]|KT1)[a-zA-Z0-9]{33}$/,description:"Tezos address must start with tz1/tz2/tz3 (implicit) or KT1 (contract)"},other:{pattern:/^.{10,100}$/,description:"Address format varies by network (10-100 characters)"}},r=["https:"],s=["localhost","127.0.0.1"],i={public:"pk_",secret:"sk_"};function n(e){return e?e.includes(i.secret)?{valid:!1,error:"Secret API keys (sk_) cannot be used in the SDK. Use a public key (pk_) instead. Secret keys should only be used in backend server code."}:(e.includes(i.public)||console.warn("RampWidget: API key does not have a public key prefix (pk_). For security, please generate a new public key for frontend use."),{valid:!0}):{valid:!1,error:"apiKey is required"}}function o(e){if(!e)return{valid:!0};try{const t=new URL(e);return r.includes(t.protocol)||"http:"===t.protocol&&s.includes(t.hostname)?{valid:!0}:{valid:!1,error:`redirectUrl must use HTTPS (got ${t.protocol})`}}catch{return{valid:!1,error:"redirectUrl is not a valid URL"}}}function a(e,r){if(!e)return{valid:!0};const s=e.trim();if(r){const e=r.toLowerCase(),i=t[e];if(i){const{pattern:e,description:t}=i;return e.test(s)?{valid:!0}:{valid:!1,error:t}}return s.length>=10&&s.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}return s.length>=10&&s.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}function c(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const r=new URL(e);return"localhost"===r.hostname||"127.0.0.1"===r.hostname}catch{return!1}}class d{constructor(e){this.iframe=null,this.modalContainer=null,this.boundMessageHandler=null,this.isOpen=!1,this.config={widgetUrl:e.widgetUrl||"https://widget.nowramp.com/widget/latest/index.html",apiUrl:e.apiUrl||"https://api.nowramp.com",flowType:"buy",theme:"light",...e},this.validateConfig()}open(){this.isOpen?console.warn("RampWidget: Widget is already open"):(this.isOpen=!0,this.setupMessageListener(),this.createIframe())}close(){this.isOpen&&(this.isOpen=!1,this.cleanup(),this.config.onClose?.())}setAmount(e){this.sendCommand("SET_AMOUNT",e)}setTheme(e){this.sendCommand("SET_THEME",e)}refreshQuote(){this.sendCommand("REFRESH_QUOTE")}validateConfig(){if(!this.config.apiKey)throw new Error("RampWidget: apiKey is required");if(!this.config.projectId)throw new Error("RampWidget: projectId is required");if(!!(!this.config.sessionId||!this.config.clientSecret)&&!this.config.externalUserId)throw new Error("RampWidget: Either externalUserId (legacy) or sessionId + clientSecret (recommended) is required");if(this.config.sessionId&&!this.config.clientSecret)throw new Error("RampWidget: clientSecret is required when using sessionId");const e=n(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=o(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=a(this.config.walletAddress,void 0);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return this.config.sessionId&&this.config.clientSecret&&(e.set("sessionId",this.config.sessionId),e.set("clientSecret",this.config.clientSecret)),this.config.externalUserId&&e.set("externalUserId",this.config.externalUserId),this.config.apiUrl&&e.set("apiUrl",this.config.apiUrl),this.config.locale&&e.set("locale",this.config.locale),void 0!==this.config.amount&&e.set("amount",String(this.config.amount)),this.config.sourceCurrency&&e.set("sourceCurrency",this.config.sourceCurrency),this.config.destinationCurrency&&e.set("destinationCurrency",this.config.destinationCurrency),this.config.walletAddress&&e.set("walletAddress",this.config.walletAddress),this.config.network&&e.set("network",this.config.network),this.config.redirectUrl&&e.set("redirectUrl",this.config.redirectUrl),!1===this.config.autoRedirect&&e.set("autoRedirect","false"),`${this.config.widgetUrl}?${e.toString()}`}createIframe(){this.iframe=document.createElement("iframe"),this.iframe.src=this.buildWidgetUrl(),this.iframe.style.border="none",this.iframe.style.width="100%",this.iframe.style.height="100%",this.iframe.allow="camera *; microphone *; geolocation *; encrypted-media *; clipboard-read; clipboard-write; payment *",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-popups-without-user-activation allow-top-navigation allow-downloads");const e=this.getContainer();e?e.appendChild(this.iframe):this.createModal()}getContainer(){return this.config.container?"string"==typeof this.config.container?document.querySelector(this.config.container):this.config.container:null}createModal(){this.modalContainer=document.createElement("div"),this.modalContainer.style.cssText="\n position: fixed;\n inset: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n ";const e="dark"===this.config.theme,t=document.createElement("div");t.style.cssText=`\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: ${e?"#000000":"white"};\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n `,this.iframe&&t.appendChild(this.iframe),this.modalContainer.appendChild(t),this.modalContainer.onclick=e=>{e.target===this.modalContainer&&this.close()};document.addEventListener("keydown",e=>{"Escape"===e.key&&this.close()}),document.body.appendChild(this.modalContainer),document.body.style.overflow="hidden"}setupMessageListener(){this.boundMessageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.boundMessageHandler)}handleMessage(e){if(!c(e.origin,this.config.widgetUrl))return;const t=e.data;if(t&&"ramp-widget"===t.source)switch(t.type){case"WIDGET_READY":this.config.onReady?.();break;case"WIDGET_CLOSE":this.close();break;case"STEP_CHANGE":this.config.onStepChange?.(t.payload.step);break;case"QUOTE_CREATED":this.config.onQuoteCreated?.(t.payload);break;case"ORDER_CREATED":this.config.onOrderCreated?.(t.payload);break;case"ORDER_COMPLETED":this.config.onSuccess?.(t.payload);break;case"ERROR":this.config.onError?.(t.payload)}}sendCommand(e,t){if(!this.iframe?.contentWindow)return void console.warn("RampWidget: Cannot send command, widget not open");const r=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},r)}cleanup(){this.boundMessageHandler&&(window.removeEventListener("message",this.boundMessageHandler),this.boundMessageHandler=null),this.iframe?.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.modalContainer?.parentNode&&(this.modalContainer.parentNode.removeChild(this.modalContainer),document.body.style.overflow=""),this.modalContainer=null}}class l{constructor(e){this.config=e,this.baseUrl=e.apiUrl||"https://api.nowramp.com"}async getRate(e){if(!this.config.apiKey)return this.getPublicRate(e);const t=await fetch(`${this.baseUrl}/v1/rates/convert`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{rate:s.rate,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationAmount:s.destinationAmount,destinationCurrency:s.destinationCurrency,fees:{processingFee:s.fees?.processingFee||0,processingFeePercent:s.fees?.processingFeePercent||0,networkFee:s.fees?.networkFee||0,totalFee:s.fees?.totalFee||0},provider:s.provider||"unknown",expiresAt:s.expiresAt}}async getPublicRate(e){const t=await fetch(`${this.baseUrl}/public/wert/convert`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{rate:s.ticker||s.rate,sourceAmount:s.currencyAmount||s.sourceAmount||e.amount,sourceCurrency:e.from,destinationAmount:s.commodityAmount||s.destinationAmount,destinationCurrency:e.to,fees:{processingFee:s.feeAmount||s.fees?.processingFee||0,processingFeePercent:s.feePercent||s.fees?.processingFeePercent||0,networkFee:s.currencyMinerFee||s.fees?.networkFee||0,totalFee:(s.feeAmount||0)+(s.currencyMinerFee||0)||s.fees?.totalFee||0},provider:"wert",expiresAt:s.expiresAt}}async getCurrencies(){if(!this.config.apiKey)return this.getPublicCurrencies();const e=await fetch(`${this.baseUrl}/v1/rates/currencies`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${r}`)}const t=await e.json(),r=t.data?.attributes||t.data||t;return{fiat:r.fiat||[],crypto:r.crypto||[],provider:r.provider||"unknown",environment:r.environment||"sandbox"}}async getPublicCurrencies(){if(!this.config.projectId)throw new Error("projectId is required to fetch currencies without an API key");const e=await fetch(`${this.baseUrl}/public/projects/${this.config.projectId}/currencies`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${r}`)}const t=await e.json(),r=t.data?.attributes||t.data||t;return{fiat:r.fiat||[],crypto:r.crypto||[],provider:r.provider||"unknown",environment:r.environment||"sandbox"}}async validateAddress(e){const t=await fetch(`${this.baseUrl}/public/validate/address`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate address: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async validateAddressBatch(e){if(e.length>20)throw new Error("Maximum 20 addresses per batch");const t=await fetch(`${this.baseUrl}/public/validate/address/batch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({addresses:e})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate addresses: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async getSupportedNetworks(){const e=await fetch(`${this.baseUrl}/public/validate/networks`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get supported networks: ${r}`)}const t=await e.json();return t.data?.attributes||t.data||t}async createSession(e){if(!this.config.apiKey)throw new Error("API key is required to create sessions. Use a secret key (sk_) for this operation.");const t=await fetch(`${this.baseUrl}/v1/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify(e)});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to create session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,clientSecret:s.clientSecret,type:s.type||"onramp",status:s.status||"pending",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,expiresAt:s.expiresAt,createdAt:s.createdAt}}async getSession(e){if(!this.config.apiKey)throw new Error("API key is required to get session details.");const t=await fetch(`${this.baseUrl}/v1/sessions/${e}`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,type:s.type||"onramp",status:s.status||"pending",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,fieldLocks:s.fieldLocks,orderId:s.orderId,expiresAt:s.expiresAt,confirmedAt:s.confirmedAt,completedAt:s.completedAt,createdAt:s.createdAt}}async cancelSession(e){if(!this.config.apiKey)throw new Error("API key is required to cancel sessions. Use a secret key (sk_) for this operation.");const t=await fetch(`${this.baseUrl}/v1/sessions/${e}/cancel`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to cancel session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,type:s.type||"onramp",status:s.status||"cancelled",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,orderId:s.orderId,expiresAt:s.expiresAt,confirmedAt:s.confirmedAt,completedAt:s.completedAt,createdAt:s.createdAt}}}e.ADDRESS_PATTERNS=t,e.ALLOWED_PROTOCOLS=r,e.API_KEY_PREFIXES=i,e.DEV_ALLOWED_HOSTNAMES=s,e.RampApi=l,e.RampWidget=d,e.VERSION="0.1.13",e.default=d,e.getCurrencies=async function(e){return new l(e).getCurrencies()},e.getRate=async function(e,t){return new l(e).getRate(t)},e.getSupportedNetworks=async function(e){return new l({apiKey:"",projectId:"",...e}).getSupportedNetworks()},e.validateAddress=async function(e,t){return new l({apiKey:"",projectId:"",...e}).validateAddress(t)},e.validateAddressBatch=async function(e,t){return new l({apiKey:"",projectId:"",...e}).validateAddressBatch(t)},e.validateApiKey=n,e.validateMessageOrigin=c,e.validateRedirectUrl=o,e.validateWalletAddress=a,Object.defineProperty(e,"__esModule",{value:!0})});
6
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).NowRamp={})}(this,function(e){"use strict";const t={evm:{pattern:/^0x[a-fA-F0-9]{40}$/,description:"EVM address must start with 0x followed by 40 hexadecimal characters"},solana:{pattern:/^[1-9A-HJ-NP-Za-km-z]{32,44}$/,description:"Solana address must be 32-44 base58 characters"},bitcoin:{pattern:/^(1[a-km-zA-HJ-NP-Z1-9]{25,34}|3[a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})$/,description:"Bitcoin address must be a valid legacy, SegWit, or native SegWit format"},tezos:{pattern:/^(tz[123]|KT1)[a-zA-Z0-9]{33}$/,description:"Tezos address must start with tz1/tz2/tz3 (implicit) or KT1 (contract)"},other:{pattern:/^.{10,100}$/,description:"Address format varies by network (10-100 characters)"}},r=["https:"],s=["localhost","127.0.0.1"],o={public:"pk_",secret:"sk_"};function n(e){return e?e.includes(o.secret)?{valid:!1,error:"Secret API keys (sk_) cannot be used in the SDK. Use a public key (pk_) instead. Secret keys should only be used in backend server code."}:(e.includes(o.public)||console.warn("RampWidget: API key does not have a public key prefix (pk_). For security, please generate a new public key for frontend use."),{valid:!0}):{valid:!1,error:"apiKey is required"}}function i(e){if(!e)return{valid:!0};try{const t=new URL(e);return r.includes(t.protocol)||"http:"===t.protocol&&s.includes(t.hostname)?{valid:!0}:{valid:!1,error:`redirectUrl must use HTTPS (got ${t.protocol})`}}catch{return{valid:!1,error:"redirectUrl is not a valid URL"}}}function a(e,r){if(!e)return{valid:!0};const s=e.trim();if(r){const e=r.toLowerCase(),o=t[e];if(o){const{pattern:e,description:t}=o;return e.test(s)?{valid:!0}:{valid:!1,error:t}}return s.length>=10&&s.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}return s.length>=10&&s.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}function c(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const r=new URL(e);return"localhost"===r.hostname||"127.0.0.1"===r.hostname}catch{return!1}}class d{constructor(e){this.iframe=null,this.modalContainer=null,this.boundMessageHandler=null,this.isOpen=!1,this.config={widgetUrl:e.widgetUrl||"https://widget.nowramp.com/widget/latest/index.html",apiUrl:e.apiUrl||"https://api.nowramp.com",flowType:"buy",theme:"light",...e},this.validateConfig()}open(){this.isOpen?console.warn("RampWidget: Widget is already open"):(this.isOpen=!0,this.setupMessageListener(),this.createIframe())}close(){this.isOpen&&(this.isOpen=!1,this.cleanup(),this.config.onClose?.())}setAmount(e){this.sendCommand("SET_AMOUNT",e)}setTheme(e){this.sendCommand("SET_THEME",e)}refreshQuote(){this.sendCommand("REFRESH_QUOTE")}validateConfig(){if(!this.config.apiKey)throw new Error("RampWidget: apiKey is required");if(!this.config.projectId)throw new Error("RampWidget: projectId is required");if(!!(!this.config.sessionId||!this.config.clientSecret)&&!this.config.externalUserId)throw new Error("RampWidget: Either externalUserId (legacy) or sessionId + clientSecret (recommended) is required");if(this.config.sessionId&&!this.config.clientSecret)throw new Error("RampWidget: clientSecret is required when using sessionId");const e=n(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=i(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=a(this.config.walletAddress,void 0);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return this.config.sessionId&&this.config.clientSecret&&(e.set("sessionId",this.config.sessionId),e.set("clientSecret",this.config.clientSecret)),this.config.externalUserId&&e.set("externalUserId",this.config.externalUserId),this.config.apiUrl&&e.set("apiUrl",this.config.apiUrl),this.config.locale&&e.set("locale",this.config.locale),void 0!==this.config.amount&&e.set("amount",String(this.config.amount)),this.config.sourceCurrency&&e.set("sourceCurrency",this.config.sourceCurrency),this.config.destinationCurrency&&e.set("destinationCurrency",this.config.destinationCurrency),this.config.walletAddress&&e.set("walletAddress",this.config.walletAddress),this.config.network&&e.set("network",this.config.network),this.config.redirectUrl&&e.set("redirectUrl",this.config.redirectUrl),!1===this.config.autoRedirect&&e.set("autoRedirect","false"),`${this.config.widgetUrl}?${e.toString()}`}createIframe(){this.iframe=document.createElement("iframe"),this.iframe.src=this.buildWidgetUrl(),this.iframe.style.border="none",this.iframe.style.width="100%",this.iframe.style.height="100%",this.iframe.allow="camera *; microphone *; geolocation *; encrypted-media *; clipboard-read; clipboard-write; payment *",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-popups-without-user-activation allow-top-navigation allow-downloads");const e=this.getContainer();e?e.appendChild(this.iframe):this.createModal()}getContainer(){return this.config.container?"string"==typeof this.config.container?document.querySelector(this.config.container):this.config.container:null}createModal(){this.modalContainer=document.createElement("div"),this.modalContainer.style.cssText="\n position: fixed;\n inset: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n ";const e="dark"===this.config.theme,t=document.createElement("div");t.style.cssText=`\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: ${e?"#000000":"white"};\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n `,this.iframe&&t.appendChild(this.iframe),this.modalContainer.appendChild(t),this.modalContainer.onclick=e=>{e.target===this.modalContainer&&this.close()};document.addEventListener("keydown",e=>{"Escape"===e.key&&this.close()}),document.body.appendChild(this.modalContainer),document.body.style.overflow="hidden"}setupMessageListener(){this.boundMessageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.boundMessageHandler)}handleMessage(e){if(!c(e.origin,this.config.widgetUrl))return;const t=e.data;if(t&&"ramp-widget"===t.source)switch(t.type){case"WIDGET_READY":this.config.onReady?.();break;case"WIDGET_CLOSE":this.close();break;case"STEP_CHANGE":this.config.onStepChange?.(t.payload.step);break;case"QUOTE_CREATED":this.config.onQuoteCreated?.(t.payload);break;case"ORDER_CREATED":this.config.onOrderCreated?.(t.payload);break;case"ORDER_COMPLETED":this.config.onSuccess?.(t.payload);break;case"ERROR":this.config.onError?.(t.payload)}}sendCommand(e,t){if(!this.iframe?.contentWindow)return void console.warn("RampWidget: Cannot send command, widget not open");const r=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},r)}cleanup(){this.boundMessageHandler&&(window.removeEventListener("message",this.boundMessageHandler),this.boundMessageHandler=null),this.iframe?.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.modalContainer?.parentNode&&(this.modalContainer.parentNode.removeChild(this.modalContainer),document.body.style.overflow=""),this.modalContainer=null}}class l{constructor(e){this.config=e,this.baseUrl=e.apiUrl||"https://api.nowramp.com"}async getRate(e){if(!this.config.apiKey)return this.getPublicRate(e);const t=await fetch(`${this.baseUrl}/v1/rates/convert`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{rate:s.rate,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationAmount:s.destinationAmount,destinationCurrency:s.destinationCurrency,fees:{processingFee:s.fees?.processingFee||0,processingFeePercent:s.fees?.processingFeePercent||0,networkFee:s.fees?.networkFee||0,totalFee:s.fees?.totalFee||0},provider:s.provider||"unknown",expiresAt:s.expiresAt}}async getPublicRate(e){const t=await fetch(`${this.baseUrl}/public/wert/convert`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{rate:s.ticker||s.rate,sourceAmount:s.currencyAmount||s.sourceAmount||e.amount,sourceCurrency:e.from,destinationAmount:s.commodityAmount||s.destinationAmount,destinationCurrency:e.to,fees:{processingFee:s.feeAmount||s.fees?.processingFee||0,processingFeePercent:s.feePercent||s.fees?.processingFeePercent||0,networkFee:s.currencyMinerFee||s.fees?.networkFee||0,totalFee:(s.feeAmount||0)+(s.currencyMinerFee||0)||s.fees?.totalFee||0},provider:"wert",expiresAt:s.expiresAt}}async getCurrencies(){if(!this.config.apiKey)return this.getPublicCurrencies();const e=await fetch(`${this.baseUrl}/v1/rates/currencies`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${r}`)}const t=await e.json(),r=t.data?.attributes||t.data||t;return{fiat:r.fiat||[],crypto:r.crypto||[],provider:r.provider||"unknown",environment:r.environment||"sandbox"}}async getPublicCurrencies(){if(!this.config.projectId)throw new Error("projectId is required to fetch currencies without an API key");const e=await fetch(`${this.baseUrl}/public/projects/${this.config.projectId}/currencies`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${r}`)}const t=await e.json(),r=t.data?.attributes||t.data||t;return{fiat:r.fiat||[],crypto:r.crypto||[],provider:r.provider||"unknown",environment:r.environment||"sandbox"}}async validateAddress(e){const t=await fetch(`${this.baseUrl}/public/validate/address`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate address: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async validateAddressBatch(e){if(e.length>20)throw new Error("Maximum 20 addresses per batch");const t=await fetch(`${this.baseUrl}/public/validate/address/batch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({addresses:e})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate addresses: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async getSupportedNetworks(){const e=await fetch(`${this.baseUrl}/public/validate/networks`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get supported networks: ${r}`)}const t=await e.json();return t.data?.attributes||t.data||t}async createSession(e){if(!this.config.apiKey)throw new Error("API key is required to create sessions. Use a secret key (sk_) for this operation.");const t=await fetch(`${this.baseUrl}/v1/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify(e)});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to create session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,clientSecret:s.clientSecret,type:s.type||"onramp",status:s.status||"pending",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,expiresAt:s.expiresAt,createdAt:s.createdAt}}async getSession(e){if(!this.config.apiKey)throw new Error("API key is required to get session details.");const t=await fetch(`${this.baseUrl}/v1/sessions/${e}`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,type:s.type||"onramp",status:s.status||"pending",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,fieldLocks:s.fieldLocks,orderId:s.orderId,expiresAt:s.expiresAt,confirmedAt:s.confirmedAt,completedAt:s.completedAt,createdAt:s.createdAt}}async cancelSession(e){if(!this.config.apiKey)throw new Error("API key is required to cancel sessions. Use a secret key (sk_) for this operation.");const t=await fetch(`${this.baseUrl}/v1/sessions/${e}/cancel`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to cancel session: ${r}`)}const r=await t.json(),s=r.data?.attributes||r.data||r;return{sessionId:r.data?.id||s.sessionId,type:s.type||"onramp",status:s.status||"cancelled",customerId:s.customerId,externalCustomerId:s.externalCustomerId,sourceAmount:s.sourceAmount,sourceCurrency:s.sourceCurrency,destinationCurrency:s.destinationCurrency,destinationAddress:s.destinationAddress,network:s.network,redirectUrl:s.redirectUrl,metadata:s.metadata,orderId:s.orderId,expiresAt:s.expiresAt,confirmedAt:s.confirmedAt,completedAt:s.completedAt,createdAt:s.createdAt}}async getSupported(e){if(!this.config.projectId)throw new Error("projectId is required to get supported config");const t=new URLSearchParams({projectId:this.config.projectId});e&&t.set("orderType",e);const r=await fetch(`${this.baseUrl}/public/v1/onramp/supported?${t}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!r.ok){const e=await r.json().catch(()=>({})),t=e.error?.message||e.message||`HTTP ${r.status}`;throw new Error(`Failed to get supported config: ${t}`)}const s=await r.json();return s.data?.attributes||s.data||s}async getQuotes(e){if(!this.config.projectId)throw new Error("projectId is required to get quotes");const t=new URLSearchParams({projectId:this.config.projectId,fiatCurrency:e.fiatCurrency,cryptoCurrency:e.cryptoCurrency,network:e.network});e.fiatAmount&&t.set("fiatAmount",e.fiatAmount),e.cryptoAmount&&t.set("cryptoAmount",e.cryptoAmount),e.orderType&&t.set("orderType",e.orderType),e.paymentMethodId&&t.set("paymentMethodId",e.paymentMethodId),e.country&&t.set("country",e.country);const r=await fetch(`${this.baseUrl}/public/v1/onramp/quotes?${t}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!r.ok){const e=await r.json().catch(()=>({})),t=e.error?.message||e.message||`HTTP ${r.status}`;throw new Error(`Failed to get quotes: ${t}`)}const s=await r.json();return s.data?.attributes||s.data||s}async createCheckoutIntent(e){if(!this.config.projectId)throw new Error("projectId is required to create checkout intents");const t=await fetch(`${this.baseUrl}/public/v1/onramp/checkout-intent`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectId:this.config.projectId,...e})});if(!t.ok){const e=await t.json().catch(()=>({})),r=("string"==typeof e.error?e.error:e.error?.message)||e.message||`HTTP ${t.status}`;let s="";if(e.details)try{const t="string"==typeof e.details?JSON.parse(e.details):e.details;if(t.errors&&"object"==typeof t.errors){const e=Object.values(t.errors).flat().filter(Boolean);e.length&&(s=e.join("; "))}}catch{}throw new Error(`Failed to create checkout intent: ${s||r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async getTransaction(e){const t=await fetch(`${this.baseUrl}/public/v1/onramp/transactions/${e}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get transaction: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}}e.ADDRESS_PATTERNS=t,e.ALLOWED_PROTOCOLS=r,e.API_KEY_PREFIXES=o,e.DEV_ALLOWED_HOSTNAMES=s,e.RampApi=l,e.RampWidget=d,e.VERSION="0.1.13",e.createCheckoutIntent=async function(e,t){return new l({apiKey:"",...e}).createCheckoutIntent(t)},e.default=d,e.getCurrencies=async function(e){return new l(e).getCurrencies()},e.getQuotes=async function(e,t){return new l({apiKey:"",...e}).getQuotes(t)},e.getRate=async function(e,t){return new l(e).getRate(t)},e.getSupported=async function(e,t){return new l({apiKey:"",...e}).getSupported(t)},e.getSupportedNetworks=async function(e){return new l({apiKey:"",projectId:"",...e}).getSupportedNetworks()},e.getTransaction=async function(e,t){return new l({apiKey:"",projectId:"",...e}).getTransaction(t)},e.validateAddress=async function(e,t){return new l({apiKey:"",projectId:"",...e}).validateAddress(t)},e.validateAddressBatch=async function(e,t){return new l({apiKey:"",projectId:"",...e}).validateAddressBatch(t)},e.validateAmount=function(e,t,r,s){if(!e||""===e.trim())return{valid:!1,error:"Amount is required"};const o=parseFloat(e);if(isNaN(o))return{valid:!1,error:"Amount must be a valid number"};if(o<=0)return{valid:!1,error:"Amount must be greater than zero"};const n=s?` ${s}`:"";return o<t?{valid:!1,error:`Minimum amount is ${t}${n}`}:o>r?{valid:!1,error:`Maximum amount is ${r}${n}`}:{valid:!0}},e.validateApiKey=n,e.validateMessageOrigin=c,e.validateRedirectUrl=i,e.validateWalletAddress=a,Object.defineProperty(e,"__esModule",{value:!0})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowramp/sdk",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "description": "Official SDK for embedding the NowRamp fiat-to-crypto widget",
5
5
  "author": "NowRamp <support@nowramp.com>",
6
6
  "license": "MIT",