@nowramp/sdk 0.1.6 → 0.1.9

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
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * @nowramp/sdk v0.1.6
2
+ * @nowramp/sdk v0.1.9
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"}},t={ETH:"evm",USDC:"evm",USDT:"evm",DAI:"evm",WETH:"evm",WBTC:"evm",MATIC:"evm",SOL:"solana",BTC:"bitcoin"},i=["https:"],n=["localhost","127.0.0.1"],r={public:"pk_",secret:"sk_"};function o(e){return e?e.includes(r.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(r.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 s(e){if(!e)return{valid:!0};try{const t=new URL(e);return i.includes(t.protocol)||"http:"===t.protocol&&n.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(i,n){if(!i)return{valid:!0};const r=i.trim();if(n){const i=t[n.toUpperCase()];if(i){const{pattern:t,description:n}=e[i];return t.test(r)?{valid:!0}:{valid:!1,error:n}}}for(const{pattern:t}of Object.values(e))if(t.test(r))return{valid:!0};return r.length>=10&&r.length<=100?{valid:!0}:{valid:!1,error:"walletAddress format is not recognized"}}function d(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const i=new URL(e);return"localhost"===i.hostname||"127.0.0.1"===i.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.externalUserId)throw new Error("RampWidget: externalUserId is required");const e=o(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=s(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=a(this.config.walletAddress,this.config.destinationCurrency);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,externalUserId:this.config.externalUserId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return 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.provider&&e.set("provider",this.config.provider),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",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals");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=document.createElement("div");e.style.cssText="\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: white;\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n ";const t=document.createElement("button");t.innerHTML="&times;",t.style.cssText="\n position: absolute;\n top: 8px;\n right: 8px;\n z-index: 10;\n width: 32px;\n height: 32px;\n border: none;\n background: rgba(0, 0, 0, 0.1);\n border-radius: 50%;\n font-size: 20px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n ",t.onclick=()=>this.close(),e.appendChild(t),this.iframe&&e.appendChild(this.iframe),this.modalContainer.appendChild(e),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(!d(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 i=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},i)}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}}exports.ADDRESS_PATTERNS=e,exports.ALLOWED_PROTOCOLS=i,exports.API_KEY_PREFIXES=r,exports.CURRENCY_CHAIN_MAP=t,exports.DEV_ALLOWED_HOSTNAMES=n,exports.RampWidget=c,exports.VERSION="0.1.6",exports.default=c,exports.validateApiKey=o,exports.validateMessageOrigin=d,exports.validateRedirectUrl=s,exports.validateWalletAddress=a;
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"}},t={ETH:"evm",USDC:"evm",USDT:"evm",DAI:"evm",WETH:"evm",WBTC:"evm",MATIC:"evm",SOL:"solana",BTC:"bitcoin"},i=["https:"],r=["localhost","127.0.0.1"],n={public:"pk_",secret:"sk_"};function o(e){return e?e.includes(n.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(n.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 s(e){if(!e)return{valid:!0};try{const t=new URL(e);return i.includes(t.protocol)||"http:"===t.protocol&&r.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(i,r){if(!i)return{valid:!0};const n=i.trim();if(r){const i=t[r.toUpperCase()];if(i){const{pattern:t,description:r}=e[i];return t.test(n)?{valid:!0}:{valid:!1,error:r}}}for(const{pattern:t}of Object.values(e))if(t.test(n))return{valid:!0};return n.length>=10&&n.length<=100?{valid:!0}:{valid:!1,error:"walletAddress format is not recognized"}}function c(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const i=new URL(e);return"localhost"===i.hostname||"127.0.0.1"===i.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.externalUserId)throw new Error("RampWidget: externalUserId is required");const e=o(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=s(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=a(this.config.walletAddress,this.config.destinationCurrency);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,externalUserId:this.config.externalUserId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return 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.provider&&e.set("provider",this.config.provider),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",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals");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=document.createElement("div");e.style.cssText="\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: white;\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n ",this.iframe&&e.appendChild(this.iframe),this.modalContainer.appendChild(e),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 i=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},i)}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){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(()=>({})),i=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${i}`)}const i=await t.json(),r=i.data?.attributes||i.data||i;return{rate:r.rate,sourceAmount:r.sourceAmount,sourceCurrency:r.sourceCurrency,destinationAmount:r.destinationAmount,destinationCurrency:r.destinationCurrency,fees:{processingFee:r.fees?.processingFee||0,processingFeePercent:r.fees?.processingFeePercent||0,networkFee:r.fees?.networkFee||0,totalFee:r.fees?.totalFee||0},provider:r.provider||"unknown",expiresAt:r.expiresAt}}async getCurrencies(){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(()=>({})),i=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${i}`)}const t=await e.json(),i=t.data?.attributes||t.data||t;return{fiat:i.fiat||[],crypto:i.crypto||[],provider:i.provider||"unknown"}}}exports.ADDRESS_PATTERNS=e,exports.ALLOWED_PROTOCOLS=i,exports.API_KEY_PREFIXES=n,exports.CURRENCY_CHAIN_MAP=t,exports.DEV_ALLOWED_HOSTNAMES=r,exports.RampApi=l,exports.RampWidget=d,exports.VERSION="0.1.9",exports.default=d,exports.getCurrencies=async function(e){return new l(e).getCurrencies()},exports.getRate=async function(e,t){return new l(e).getRate(t)},exports.validateApiKey=o,exports.validateMessageOrigin=c,exports.validateRedirectUrl=s,exports.validateWalletAddress=a;
package/dist/index.d.ts CHANGED
@@ -88,6 +88,99 @@ interface ValidationResult {
88
88
  valid: boolean;
89
89
  error?: string;
90
90
  }
91
+ /**
92
+ * Request parameters for getting conversion rates
93
+ */
94
+ interface GetRateParams {
95
+ /** Source currency code (e.g., 'USD' for buy, 'ETH' for sell) */
96
+ from: string;
97
+ /** Destination currency code (e.g., 'ETH' for buy, 'USD' for sell) */
98
+ to: string;
99
+ /** Amount in source currency */
100
+ amount: number;
101
+ /** Flow type: buy (fiat->crypto) or sell (crypto->fiat) */
102
+ flowType?: FlowType;
103
+ }
104
+ /**
105
+ * Conversion rate result from the API
106
+ */
107
+ interface RateResult {
108
+ /** Exchange rate: 1 destination = X source */
109
+ rate: number;
110
+ /** Source currency and amount */
111
+ sourceAmount: number;
112
+ sourceCurrency: string;
113
+ /** Destination currency and amount (after fees) */
114
+ destinationAmount: number;
115
+ destinationCurrency: string;
116
+ /** Fee breakdown */
117
+ fees: {
118
+ /** Processing fee in source currency */
119
+ processingFee: number;
120
+ /** Processing fee as decimal (0.05 = 5%) */
121
+ processingFeePercent: number;
122
+ /** Network fee in source currency */
123
+ networkFee: number;
124
+ /** Total fees in source currency */
125
+ totalFee: number;
126
+ };
127
+ /** Provider that returned this rate */
128
+ provider: string;
129
+ /** Rate expiry timestamp (optional) */
130
+ expiresAt?: string;
131
+ }
132
+ /**
133
+ * Configuration for standalone API calls (not widget)
134
+ */
135
+ interface RampApiConfig {
136
+ /** Partner API key (required) */
137
+ apiKey: string;
138
+ /** Project ID (required) */
139
+ projectId: string;
140
+ /** API base URL (optional, defaults to https://api.nowramp.com) */
141
+ apiUrl?: string;
142
+ }
143
+ /**
144
+ * Fiat currency info
145
+ */
146
+ interface FiatCurrency {
147
+ /** Currency code (e.g., USD, EUR) */
148
+ code: string;
149
+ /** Display name */
150
+ name: string;
151
+ /** Currency symbol (e.g., $, €) */
152
+ symbol: string;
153
+ }
154
+ /**
155
+ * Crypto currency info
156
+ */
157
+ interface CryptoCurrency {
158
+ /** Unique identifier: symbol_network */
159
+ id: string;
160
+ /** Token symbol (e.g., ETH, BTC) */
161
+ symbol: string;
162
+ /** Display name */
163
+ name: string;
164
+ /** Network/chain identifier */
165
+ network: string;
166
+ /** Chain type for address validation (evm, bitcoin, solana) */
167
+ chainType: ChainType;
168
+ /** Address placeholder for UI */
169
+ addressPlaceholder: string;
170
+ /** Example address for UI hint */
171
+ addressExample: string;
172
+ }
173
+ /**
174
+ * Available currencies from the provider
175
+ */
176
+ interface AvailableCurrencies {
177
+ /** Supported fiat currencies */
178
+ fiat: FiatCurrency[];
179
+ /** Supported crypto currencies */
180
+ crypto: CryptoCurrency[];
181
+ /** Provider that returned these currencies */
182
+ provider: string;
183
+ }
91
184
 
92
185
  /**
93
186
  * SDK Validation Utilities
@@ -194,6 +287,108 @@ declare class RampWidget {
194
287
  private cleanup;
195
288
  }
196
289
 
290
+ /**
291
+ * NowRamp API Client
292
+ * Provider-agnostic API calls for getting rates and other data
293
+ */
294
+
295
+ /**
296
+ * NowRamp API Client for making provider-agnostic API calls
297
+ *
298
+ * @example
299
+ * ```typescript
300
+ * import { RampApi } from '@nowramp/sdk';
301
+ *
302
+ * const api = new RampApi({
303
+ * apiKey: 'ramp_pk_xxx',
304
+ * projectId: 'proj_xxx',
305
+ * });
306
+ *
307
+ * const rate = await api.getRate({
308
+ * from: 'USD',
309
+ * to: 'ETH',
310
+ * amount: 100,
311
+ * });
312
+ * console.log(`You'll receive ${rate.destinationAmount} ${rate.destinationCurrency}`);
313
+ * ```
314
+ */
315
+ declare class RampApi {
316
+ private config;
317
+ private baseUrl;
318
+ constructor(config: RampApiConfig);
319
+ /**
320
+ * Get conversion rate from configured payment provider
321
+ *
322
+ * @param params - Rate request parameters
323
+ * @returns Conversion rate with fee breakdown
324
+ *
325
+ * @example
326
+ * ```typescript
327
+ * // Get rate for buying ETH with USD
328
+ * const buyRate = await api.getRate({
329
+ * from: 'USD',
330
+ * to: 'ETH',
331
+ * amount: 100,
332
+ * flowType: 'buy',
333
+ * });
334
+ *
335
+ * // Get rate for selling ETH to USD
336
+ * const sellRate = await api.getRate({
337
+ * from: 'ETH',
338
+ * to: 'USD',
339
+ * amount: 0.1,
340
+ * flowType: 'sell',
341
+ * });
342
+ * ```
343
+ */
344
+ getRate(params: GetRateParams): Promise<RateResult>;
345
+ /**
346
+ * Get available currencies from configured payment provider
347
+ * Returns fiat and crypto currencies filtered by project settings
348
+ *
349
+ * @returns Available fiat and crypto currencies
350
+ *
351
+ * @example
352
+ * ```typescript
353
+ * const currencies = await api.getCurrencies();
354
+ *
355
+ * console.log('Available fiat:', currencies.fiat.map(f => f.code));
356
+ * console.log('Available crypto:', currencies.crypto.map(c => c.symbol));
357
+ * ```
358
+ */
359
+ getCurrencies(): Promise<AvailableCurrencies>;
360
+ }
361
+ /**
362
+ * Standalone function to get conversion rate
363
+ * Convenience wrapper for one-off rate checks
364
+ *
365
+ * @example
366
+ * ```typescript
367
+ * import { getRate } from '@nowramp/sdk';
368
+ *
369
+ * const rate = await getRate(
370
+ * { apiKey: 'ramp_pk_xxx', projectId: 'proj_xxx' },
371
+ * { from: 'USD', to: 'ETH', amount: 100 }
372
+ * );
373
+ * ```
374
+ */
375
+ declare function getRate(config: RampApiConfig, params: GetRateParams): Promise<RateResult>;
376
+ /**
377
+ * Standalone function to get available currencies
378
+ * Convenience wrapper for one-off currency fetching
379
+ *
380
+ * @example
381
+ * ```typescript
382
+ * import { getCurrencies } from '@nowramp/sdk';
383
+ *
384
+ * const currencies = await getCurrencies({
385
+ * apiKey: 'ramp_pk_xxx',
386
+ * projectId: 'proj_xxx'
387
+ * });
388
+ * ```
389
+ */
390
+ declare function getCurrencies(config: RampApiConfig): Promise<AvailableCurrencies>;
391
+
197
392
  /**
198
393
  * NowRamp Widget SDK
199
394
  *
@@ -219,5 +414,5 @@ declare class RampWidget {
219
414
  */
220
415
  declare const VERSION: string;
221
416
 
222
- export { ADDRESS_PATTERNS, ALLOWED_PROTOCOLS, API_KEY_PREFIXES, CURRENCY_CHAIN_MAP, DEV_ALLOWED_HOSTNAMES, RampWidget, VERSION, RampWidget as default, validateApiKey, validateMessageOrigin, validateRedirectUrl, validateWalletAddress };
223
- export type { ChainType, ErrorData, FlowType, Locale, OrderData, QuoteData, RampWidgetConfig, Theme, ValidationResult, WidgetEventType, WidgetMessage };
417
+ export { ADDRESS_PATTERNS, ALLOWED_PROTOCOLS, API_KEY_PREFIXES, CURRENCY_CHAIN_MAP, DEV_ALLOWED_HOSTNAMES, RampApi, RampWidget, VERSION, RampWidget as default, getCurrencies, getRate, validateApiKey, validateMessageOrigin, validateRedirectUrl, validateWalletAddress };
418
+ export type { AvailableCurrencies, ChainType, CryptoCurrency, ErrorData, FiatCurrency, FlowType, GetRateParams, Locale, OrderData, QuoteData, RampApiConfig, RampWidgetConfig, RateResult, Theme, ValidationResult, WidgetEventType, WidgetMessage };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * @nowramp/sdk v0.1.6
2
+ * @nowramp/sdk v0.1.9
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"}},t={ETH:"evm",USDC:"evm",USDT:"evm",DAI:"evm",WETH:"evm",WBTC:"evm",MATIC:"evm",SOL:"solana",BTC:"bitcoin"},i=["https:"],n=["localhost","127.0.0.1"],r={public:"pk_",secret:"sk_"};function o(e){return e?e.includes(r.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(r.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 s(e){if(!e)return{valid:!0};try{const t=new URL(e);return i.includes(t.protocol)||"http:"===t.protocol&&n.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(i,n){if(!i)return{valid:!0};const r=i.trim();if(n){const i=t[n.toUpperCase()];if(i){const{pattern:t,description:n}=e[i];return t.test(r)?{valid:!0}:{valid:!1,error:n}}}for(const{pattern:t}of Object.values(e))if(t.test(r))return{valid:!0};return r.length>=10&&r.length<=100?{valid:!0}:{valid:!1,error:"walletAddress format is not recognized"}}function d(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const i=new URL(e);return"localhost"===i.hostname||"127.0.0.1"===i.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.externalUserId)throw new Error("RampWidget: externalUserId is required");const e=o(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=s(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=a(this.config.walletAddress,this.config.destinationCurrency);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,externalUserId:this.config.externalUserId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return 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.provider&&e.set("provider",this.config.provider),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",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals");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=document.createElement("div");e.style.cssText="\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: white;\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n ";const t=document.createElement("button");t.innerHTML="&times;",t.style.cssText="\n position: absolute;\n top: 8px;\n right: 8px;\n z-index: 10;\n width: 32px;\n height: 32px;\n border: none;\n background: rgba(0, 0, 0, 0.1);\n border-radius: 50%;\n font-size: 20px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n ",t.onclick=()=>this.close(),e.appendChild(t),this.iframe&&e.appendChild(this.iframe),this.modalContainer.appendChild(e),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(!d(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 i=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},i)}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}}const l="0.1.6";export{e as ADDRESS_PATTERNS,i as ALLOWED_PROTOCOLS,r as API_KEY_PREFIXES,t as CURRENCY_CHAIN_MAP,n as DEV_ALLOWED_HOSTNAMES,c as RampWidget,l as VERSION,c as default,o as validateApiKey,d as validateMessageOrigin,s as validateRedirectUrl,a 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"}},t={ETH:"evm",USDC:"evm",USDT:"evm",DAI:"evm",WETH:"evm",WBTC:"evm",MATIC:"evm",SOL:"solana",BTC:"bitcoin"},i=["https:"],n=["localhost","127.0.0.1"],r={public:"pk_",secret:"sk_"};function o(e){return e?e.includes(r.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(r.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 s(e){if(!e)return{valid:!0};try{const t=new URL(e);return i.includes(t.protocol)||"http:"===t.protocol&&n.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(i,n){if(!i)return{valid:!0};const r=i.trim();if(n){const i=t[n.toUpperCase()];if(i){const{pattern:t,description:n}=e[i];return t.test(r)?{valid:!0}:{valid:!1,error:n}}}for(const{pattern:t}of Object.values(e))if(t.test(r))return{valid:!0};return r.length>=10&&r.length<=100?{valid:!0}:{valid:!1,error:"walletAddress format is not recognized"}}function c(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const i=new URL(e);return"localhost"===i.hostname||"127.0.0.1"===i.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.externalUserId)throw new Error("RampWidget: externalUserId is required");const e=o(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=s(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=a(this.config.walletAddress,this.config.destinationCurrency);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,externalUserId:this.config.externalUserId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return 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.provider&&e.set("provider",this.config.provider),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",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals");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=document.createElement("div");e.style.cssText="\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: white;\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n ",this.iframe&&e.appendChild(this.iframe),this.modalContainer.appendChild(e),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 i=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},i)}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){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(()=>({})),i=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${i}`)}const i=await t.json(),n=i.data?.attributes||i.data||i;return{rate:n.rate,sourceAmount:n.sourceAmount,sourceCurrency:n.sourceCurrency,destinationAmount:n.destinationAmount,destinationCurrency:n.destinationCurrency,fees:{processingFee:n.fees?.processingFee||0,processingFeePercent:n.fees?.processingFeePercent||0,networkFee:n.fees?.networkFee||0,totalFee:n.fees?.totalFee||0},provider:n.provider||"unknown",expiresAt:n.expiresAt}}async getCurrencies(){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(()=>({})),i=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${i}`)}const t=await e.json(),i=t.data?.attributes||t.data||t;return{fiat:i.fiat||[],crypto:i.crypto||[],provider:i.provider||"unknown"}}}async function h(e,t){return new l(e).getRate(t)}async function p(e){return new l(e).getCurrencies()}const f="0.1.9";export{e as ADDRESS_PATTERNS,i as ALLOWED_PROTOCOLS,r as API_KEY_PREFIXES,t as CURRENCY_CHAIN_MAP,n as DEV_ALLOWED_HOSTNAMES,l as RampApi,d as RampWidget,f as VERSION,d as default,p as getCurrencies,h as getRate,o as validateApiKey,c as validateMessageOrigin,s as validateRedirectUrl,a as validateWalletAddress};
package/dist/sdk.umd.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * @nowramp/sdk v0.1.6
2
+ * @nowramp/sdk v0.1.9
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"}},i={ETH:"evm",USDC:"evm",USDT:"evm",DAI:"evm",WETH:"evm",WBTC:"evm",MATIC:"evm",SOL:"solana",BTC:"bitcoin"},n=["https:"],r=["localhost","127.0.0.1"],o={public:"pk_",secret:"sk_"};function s(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 a(e){if(!e)return{valid:!0};try{const t=new URL(e);return n.includes(t.protocol)||"http:"===t.protocol&&r.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 d(e,n){if(!e)return{valid:!0};const r=e.trim();if(n){const e=i[n.toUpperCase()];if(e){const{pattern:i,description:n}=t[e];return i.test(r)?{valid:!0}:{valid:!1,error:n}}}for(const{pattern:e}of Object.values(t))if(e.test(r))return{valid:!0};return r.length>=10&&r.length<=100?{valid:!0}:{valid:!1,error:"walletAddress format is not recognized"}}function c(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const i=new URL(e);return"localhost"===i.hostname||"127.0.0.1"===i.hostname}catch{return!1}}class l{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.externalUserId)throw new Error("RampWidget: externalUserId is required");const e=s(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=a(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=d(this.config.walletAddress,this.config.destinationCurrency);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,externalUserId:this.config.externalUserId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return 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.provider&&e.set("provider",this.config.provider),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",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals");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=document.createElement("div");e.style.cssText="\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: white;\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n ";const t=document.createElement("button");t.innerHTML="&times;",t.style.cssText="\n position: absolute;\n top: 8px;\n right: 8px;\n z-index: 10;\n width: 32px;\n height: 32px;\n border: none;\n background: rgba(0, 0, 0, 0.1);\n border-radius: 50%;\n font-size: 20px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n ",t.onclick=()=>this.close(),e.appendChild(t),this.iframe&&e.appendChild(this.iframe),this.modalContainer.appendChild(e),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 i=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},i)}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}}e.ADDRESS_PATTERNS=t,e.ALLOWED_PROTOCOLS=n,e.API_KEY_PREFIXES=o,e.CURRENCY_CHAIN_MAP=i,e.DEV_ALLOWED_HOSTNAMES=r,e.RampWidget=l,e.VERSION="0.1.6",e.default=l,e.validateApiKey=s,e.validateMessageOrigin=c,e.validateRedirectUrl=a,e.validateWalletAddress=d,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"}},i={ETH:"evm",USDC:"evm",USDT:"evm",DAI:"evm",WETH:"evm",WBTC:"evm",MATIC:"evm",SOL:"solana",BTC:"bitcoin"},n=["https:"],r=["localhost","127.0.0.1"],o={public:"pk_",secret:"sk_"};function s(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 a(e){if(!e)return{valid:!0};try{const t=new URL(e);return n.includes(t.protocol)||"http:"===t.protocol&&r.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 c(e,n){if(!e)return{valid:!0};const r=e.trim();if(n){const e=i[n.toUpperCase()];if(e){const{pattern:i,description:n}=t[e];return i.test(r)?{valid:!0}:{valid:!1,error:n}}}for(const{pattern:e}of Object.values(t))if(e.test(r))return{valid:!0};return r.length>=10&&r.length<=100?{valid:!0}:{valid:!1,error:"walletAddress format is not recognized"}}function d(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const i=new URL(e);return"localhost"===i.hostname||"127.0.0.1"===i.hostname}catch{return!1}}class l{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.externalUserId)throw new Error("RampWidget: externalUserId is required");const e=s(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=a(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=c(this.config.walletAddress,this.config.destinationCurrency);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,externalUserId:this.config.externalUserId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return 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.provider&&e.set("provider",this.config.provider),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",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals");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=document.createElement("div");e.style.cssText="\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: white;\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n ",this.iframe&&e.appendChild(this.iframe),this.modalContainer.appendChild(e),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(!d(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 i=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},i)}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 h{constructor(e){this.config=e,this.baseUrl=e.apiUrl||"https://api.nowramp.com"}async getRate(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(()=>({})),i=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${i}`)}const i=await t.json(),n=i.data?.attributes||i.data||i;return{rate:n.rate,sourceAmount:n.sourceAmount,sourceCurrency:n.sourceCurrency,destinationAmount:n.destinationAmount,destinationCurrency:n.destinationCurrency,fees:{processingFee:n.fees?.processingFee||0,processingFeePercent:n.fees?.processingFeePercent||0,networkFee:n.fees?.networkFee||0,totalFee:n.fees?.totalFee||0},provider:n.provider||"unknown",expiresAt:n.expiresAt}}async getCurrencies(){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(()=>({})),i=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${i}`)}const t=await e.json(),i=t.data?.attributes||t.data||t;return{fiat:i.fiat||[],crypto:i.crypto||[],provider:i.provider||"unknown"}}}e.ADDRESS_PATTERNS=t,e.ALLOWED_PROTOCOLS=n,e.API_KEY_PREFIXES=o,e.CURRENCY_CHAIN_MAP=i,e.DEV_ALLOWED_HOSTNAMES=r,e.RampApi=h,e.RampWidget=l,e.VERSION="0.1.9",e.default=l,e.getCurrencies=async function(e){return new h(e).getCurrencies()},e.getRate=async function(e,t){return new h(e).getRate(t)},e.validateApiKey=s,e.validateMessageOrigin=d,e.validateRedirectUrl=a,e.validateWalletAddress=c,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.6",
3
+ "version": "0.1.9",
4
4
  "description": "Official SDK for embedding the NowRamp fiat-to-crypto widget",
5
5
  "author": "NowRamp <support@nowramp.com>",
6
6
  "license": "MIT",