@moon-x/node-sdk 0.7.1 → 0.8.0

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/README.md CHANGED
@@ -87,6 +87,38 @@ Returns `Promise<{ access: AccessTokenClaims, identity: IdentityTokenClaims | nu
87
87
 
88
88
  Canonical entry point — use this from request handlers. Cross-checks the two tokens' subjects when both are present.
89
89
 
90
+ ### `client.funding.sign(params)`
91
+
92
+ Request a signed MoonPay Buy-widget URL for a user's wallet, so your backend can drive the card-funding flow without holding MoonPay keys. Returns `Promise<MoonPaySignResponse>`.
93
+
94
+ The route is publishable-key acceptable but signs **only** for an address that belongs to the authenticated user, so you forward that user's access token per call:
95
+
96
+ ```ts
97
+ const funding = await moonx.funding.sign({
98
+ accessToken: req.headers["x-moonx-access-token"]!, // the end user's token
99
+ publicAddress: "0xUserWalletAddress",
100
+ useSandbox: true, // omit or false for production
101
+ currencyCode: "eth", // optional MoonPay presets
102
+ quoteCurrencyAmount: 50,
103
+ });
104
+
105
+ // funding.signed_url — open this for the user
106
+ // funding.external_transaction_id — correlate the purchase
107
+ // funding.provider_publishable_key — MoonPay pk the URL was signed with
108
+ ```
109
+
110
+ | Param | Type | Required | Notes |
111
+ |---|---|---|---|
112
+ | `accessToken` | `string` | Yes | The end user's MoonX access token |
113
+ | `publicAddress` | `string` | Yes | Wallet address to fund; must belong to the user |
114
+ | `useSandbox` | `boolean` | No | `true` → MoonPay sandbox; defaults to `false` |
115
+ | `currencyCode` | `string` | No | MoonPay currency to preselect |
116
+ | `quoteCurrencyAmount` | `number` | No | Pin the fiat amount; omit to let the user choose |
117
+ | `paymentMethod` | `string` | No | MoonPay payment method to preselect |
118
+ | `uiConfig` | `{ theme?, accentColor? }` | No | Widget theming |
119
+
120
+ Failures throw a typed `MoonXError` subclass: `IntegrationNotConfiguredError` (400), `UnauthorizedError` (401), `WalletOwnershipError` (403), `RateLimitedError` (429).
121
+
90
122
  ## Errors
91
123
 
92
124
  All thrown errors extend `MoonXError`, which carries a `.code` property for programmatic handling:
@@ -128,11 +160,16 @@ try {
128
160
  | `UnsupportedAlgorithmError` | `unsupported_algorithm` | Token uses something other than ES256 |
129
161
  | `JwksFetchError` | `jwks_fetch_failed` | Couldn't fetch the JWKS from MoonX |
130
162
  | `AppResolutionError` | `app_resolution_failed` | Publishable-key → app-id lookup failed |
131
- | `ConfigurationError` | `configuration_error` | Bad `MoonXClient` config (e.g. swapped pk/sk) |
163
+ | `ConfigurationError` | `configuration_error` | Bad `MoonXClient` config, or a missing required call param |
164
+ | `IntegrationNotConfiguredError` | `integration_not_configured` | MoonPay funding is not configured or is disabled for this app (400) |
165
+ | `WalletOwnershipError` | `wallet_ownership` | The wallet address does not belong to the user (403) |
166
+ | `UnauthorizedError` | `unauthorized` | Missing or invalid credentials (401) |
167
+ | `RateLimitedError` | `rate_limited` | Request was rate limited (429) |
168
+ | `RequestFailedError` | `request_failed` | Unexpected non-2xx response (carries `.status`) |
132
169
 
133
170
  ### Type imports
134
171
 
135
- `AccessTokenClaims`, `IdentityTokenClaims`, `BaseTokenClaims`, and `VerifiedSession` are also exported from `@moon-x/core/types` — the single source of truth shared with the browser + RN SDKs. Either import path works:
172
+ `AccessTokenClaims`, `IdentityTokenClaims`, `BaseTokenClaims`, `VerifiedSession`, and `MoonPaySignResponse` are also exported from `@moon-x/core/types` — the single source of truth shared with the browser + RN SDKs. Either import path works:
136
173
 
137
174
  ```ts
138
175
  // From node-sdk (convenience re-export):
@@ -148,5 +185,5 @@ Auth providers ship server SDKs partly to abstract away `jose`-style JWT librari
148
185
 
149
186
  ## Roadmap
150
187
 
151
- - **Current**: auth (`verifyAccessToken`, `verifyIdentityToken`, `verifySession`) plus `ephemeralSigners` (secret-key gated server-side signing).
152
- - **v0.2 ([MX-124](https://linear.app/moonpay/issue/MX-124))**: adds `client.tokens.*` and `client.swaps.*`.
188
+ - **Current**: auth (`verifyAccessToken`, `verifyIdentityToken`, `verifySession`), `ephemeralSigners` (secret-key gated server-side signing), and `funding` (MoonPay onramp signing).
189
+ - **Next**: adds `client.tokens.*` and `client.swaps.*`.
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { AccessTokenClaims, IdentityTokenClaims, PresenceTokenClaims, VerifiedSession } from '@moon-x/core/types';
2
- export { AccessTokenClaims, BaseTokenClaims, IdentityTokenClaims, PresenceTokenClaims, VerifiedSession } from '@moon-x/core/types';
1
+ import { AccessTokenClaims, IdentityTokenClaims, PresenceTokenClaims, VerifiedSession, MoonPaySignResponse } from '@moon-x/core/types';
2
+ export { AccessTokenClaims, BaseTokenClaims, IdentityTokenClaims, MoonPaySignResponse, PresenceTokenClaims, VerifiedSession } from '@moon-x/core/types';
3
3
  import { EphemeralSignerSignResult } from '@moon-x/core/sdk';
4
4
  export { EphemeralSignerSignError, EphemeralSignerSignResult } from '@moon-x/core/sdk';
5
5
 
@@ -143,6 +143,60 @@ declare class EphemeralSigners {
143
143
  sign(params: SignEphemeralSignerParams): Promise<EphemeralSignerSignResult>;
144
144
  }
145
145
 
146
+ interface FundingConfig {
147
+ baseUrl: string;
148
+ /** App publishable key (`moon_pk_*`). Sent as `Authorization: PublicToken`. */
149
+ publishableKey: string;
150
+ /** Override for tests — defaults to globalThis.fetch. */
151
+ fetchImpl?: typeof fetch;
152
+ }
153
+ /** Optional MoonPay widget theming, forwarded to the Buy widget. */
154
+ interface MoonPaySignUiConfig {
155
+ /** MoonPay widget theme. */
156
+ theme?: "light" | "dark";
157
+ /** Hex accent color for the widget, e.g. `#7C3AED`. */
158
+ accentColor?: string;
159
+ }
160
+ interface MoonPaySignParams {
161
+ /**
162
+ * The end-user's MoonX access token. The route refuses to sign unless
163
+ * `publicAddress` belongs to this user. Read it from your request (the
164
+ * `X-MoonX-Access-Token` header the browser SDK sends).
165
+ */
166
+ accessToken: string;
167
+ /** Wallet address that will receive the funds. Must belong to the user. */
168
+ publicAddress: string;
169
+ /**
170
+ * Route through MoonPay's sandbox (no real payment). Defaults to `false`
171
+ * (production). Requires the app's sandbox keys, or the platform sandbox
172
+ * defaults, to be configured.
173
+ */
174
+ useSandbox?: boolean;
175
+ /** MoonPay currency code to preselect, e.g. `"eth"`, `"usdc_sol"`. */
176
+ currencyCode?: string;
177
+ /**
178
+ * Pin the fiat amount to buy. Omit to let the user choose the amount inside
179
+ * the MoonPay widget.
180
+ */
181
+ quoteCurrencyAmount?: number;
182
+ /** MoonPay payment method to preselect, e.g. `"credit_debit_card"`. */
183
+ paymentMethod?: string;
184
+ /** MoonPay widget theming. */
185
+ uiConfig?: MoonPaySignUiConfig;
186
+ }
187
+ declare class Funding {
188
+ private readonly cfg;
189
+ constructor(cfg: FundingConfig);
190
+ /**
191
+ * Request a signed MoonPay Buy-widget URL for the user's wallet. Returns the
192
+ * `signed_url` to open (plus the transaction id and provider publishable key
193
+ * used to track the purchase). Throws a typed {@link MoonXError} subclass on
194
+ * failure: IntegrationNotConfiguredError (400), UnauthorizedError (401),
195
+ * WalletOwnershipError (403), RateLimitedError (429).
196
+ */
197
+ sign(params: MoonPaySignParams): Promise<MoonPaySignResponse>;
198
+ }
199
+
146
200
  /**
147
201
  * MoonXClient is the entry point for the node SDK. Construct once per
148
202
  * process and reuse across requests — the client holds in-memory caches
@@ -165,17 +219,18 @@ declare class EphemeralSigners {
165
219
  * // session.identity?.email — OIDC profile claim (when identity token sent)
166
220
  * // session.identity?.name — etc.
167
221
  *
168
- * Today this ships the `auth` namespace plus `ephemeralSigners` (the
169
- * latter is secret-key gated; see `secretKey`). The `tokens` and `swaps`
170
- * namespaces land in v0.2.
222
+ * Today this ships the `auth` namespace, `ephemeralSigners` (secret-key
223
+ * gated; see `secretKey`), and `funding` (the MoonPay onramp sign surface).
224
+ * The `tokens` and `swaps` namespaces land in a later release.
171
225
  */
172
226
  declare class MoonXClient {
173
227
  readonly auth: Auth;
174
228
  readonly ephemeralSigners: EphemeralSigners;
229
+ readonly funding: Funding;
175
230
  constructor(input: MoonXClientConfig);
176
231
  }
177
232
 
178
- type MoonXErrorCode = "malformed_token" | "invalid_signature" | "expired_token" | "audience_mismatch" | "issuer_mismatch" | "subject_mismatch" | "kid_mismatch" | "unsupported_algorithm" | "jwks_fetch_failed" | "app_resolution_failed" | "configuration_error";
233
+ type MoonXErrorCode = "malformed_token" | "invalid_signature" | "expired_token" | "audience_mismatch" | "issuer_mismatch" | "subject_mismatch" | "kid_mismatch" | "unsupported_algorithm" | "jwks_fetch_failed" | "app_resolution_failed" | "configuration_error" | "integration_not_configured" | "wallet_ownership" | "rate_limited" | "unauthorized" | "request_failed";
179
234
  declare class MoonXError extends Error {
180
235
  readonly code: MoonXErrorCode;
181
236
  constructor(code: MoonXErrorCode, message: string);
@@ -213,5 +268,21 @@ declare class AppResolutionError extends MoonXError {
213
268
  declare class ConfigurationError extends MoonXError {
214
269
  constructor(message: string);
215
270
  }
271
+ declare class IntegrationNotConfiguredError extends MoonXError {
272
+ constructor(message?: string);
273
+ }
274
+ declare class WalletOwnershipError extends MoonXError {
275
+ constructor(message?: string);
276
+ }
277
+ declare class RateLimitedError extends MoonXError {
278
+ constructor(message?: string);
279
+ }
280
+ declare class UnauthorizedError extends MoonXError {
281
+ constructor(message?: string);
282
+ }
283
+ declare class RequestFailedError extends MoonXError {
284
+ readonly status: number;
285
+ constructor(status: number, message: string);
286
+ }
216
287
 
217
- export { AppResolutionError, AudienceMismatchError, ConfigurationError, EphemeralSigners, ExpiredTokenError, InvalidTokenError, IssuerMismatchError, JwksFetchError, KidMismatchError, MalformedTokenError, MoonXClient, type MoonXClientConfig, MoonXError, type MoonXErrorCode, type SignEphemeralSignerParams, SubjectMismatchError, UnsupportedAlgorithmError };
288
+ export { AppResolutionError, AudienceMismatchError, ConfigurationError, EphemeralSigners, ExpiredTokenError, Funding, type FundingConfig, IntegrationNotConfiguredError, InvalidTokenError, IssuerMismatchError, JwksFetchError, KidMismatchError, MalformedTokenError, type MoonPaySignParams, type MoonPaySignUiConfig, MoonXClient, type MoonXClientConfig, MoonXError, type MoonXErrorCode, RateLimitedError, RequestFailedError, type SignEphemeralSignerParams, SubjectMismatchError, UnauthorizedError, UnsupportedAlgorithmError, WalletOwnershipError };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { AccessTokenClaims, IdentityTokenClaims, PresenceTokenClaims, VerifiedSession } from '@moon-x/core/types';
2
- export { AccessTokenClaims, BaseTokenClaims, IdentityTokenClaims, PresenceTokenClaims, VerifiedSession } from '@moon-x/core/types';
1
+ import { AccessTokenClaims, IdentityTokenClaims, PresenceTokenClaims, VerifiedSession, MoonPaySignResponse } from '@moon-x/core/types';
2
+ export { AccessTokenClaims, BaseTokenClaims, IdentityTokenClaims, MoonPaySignResponse, PresenceTokenClaims, VerifiedSession } from '@moon-x/core/types';
3
3
  import { EphemeralSignerSignResult } from '@moon-x/core/sdk';
4
4
  export { EphemeralSignerSignError, EphemeralSignerSignResult } from '@moon-x/core/sdk';
5
5
 
@@ -143,6 +143,60 @@ declare class EphemeralSigners {
143
143
  sign(params: SignEphemeralSignerParams): Promise<EphemeralSignerSignResult>;
144
144
  }
145
145
 
146
+ interface FundingConfig {
147
+ baseUrl: string;
148
+ /** App publishable key (`moon_pk_*`). Sent as `Authorization: PublicToken`. */
149
+ publishableKey: string;
150
+ /** Override for tests — defaults to globalThis.fetch. */
151
+ fetchImpl?: typeof fetch;
152
+ }
153
+ /** Optional MoonPay widget theming, forwarded to the Buy widget. */
154
+ interface MoonPaySignUiConfig {
155
+ /** MoonPay widget theme. */
156
+ theme?: "light" | "dark";
157
+ /** Hex accent color for the widget, e.g. `#7C3AED`. */
158
+ accentColor?: string;
159
+ }
160
+ interface MoonPaySignParams {
161
+ /**
162
+ * The end-user's MoonX access token. The route refuses to sign unless
163
+ * `publicAddress` belongs to this user. Read it from your request (the
164
+ * `X-MoonX-Access-Token` header the browser SDK sends).
165
+ */
166
+ accessToken: string;
167
+ /** Wallet address that will receive the funds. Must belong to the user. */
168
+ publicAddress: string;
169
+ /**
170
+ * Route through MoonPay's sandbox (no real payment). Defaults to `false`
171
+ * (production). Requires the app's sandbox keys, or the platform sandbox
172
+ * defaults, to be configured.
173
+ */
174
+ useSandbox?: boolean;
175
+ /** MoonPay currency code to preselect, e.g. `"eth"`, `"usdc_sol"`. */
176
+ currencyCode?: string;
177
+ /**
178
+ * Pin the fiat amount to buy. Omit to let the user choose the amount inside
179
+ * the MoonPay widget.
180
+ */
181
+ quoteCurrencyAmount?: number;
182
+ /** MoonPay payment method to preselect, e.g. `"credit_debit_card"`. */
183
+ paymentMethod?: string;
184
+ /** MoonPay widget theming. */
185
+ uiConfig?: MoonPaySignUiConfig;
186
+ }
187
+ declare class Funding {
188
+ private readonly cfg;
189
+ constructor(cfg: FundingConfig);
190
+ /**
191
+ * Request a signed MoonPay Buy-widget URL for the user's wallet. Returns the
192
+ * `signed_url` to open (plus the transaction id and provider publishable key
193
+ * used to track the purchase). Throws a typed {@link MoonXError} subclass on
194
+ * failure: IntegrationNotConfiguredError (400), UnauthorizedError (401),
195
+ * WalletOwnershipError (403), RateLimitedError (429).
196
+ */
197
+ sign(params: MoonPaySignParams): Promise<MoonPaySignResponse>;
198
+ }
199
+
146
200
  /**
147
201
  * MoonXClient is the entry point for the node SDK. Construct once per
148
202
  * process and reuse across requests — the client holds in-memory caches
@@ -165,17 +219,18 @@ declare class EphemeralSigners {
165
219
  * // session.identity?.email — OIDC profile claim (when identity token sent)
166
220
  * // session.identity?.name — etc.
167
221
  *
168
- * Today this ships the `auth` namespace plus `ephemeralSigners` (the
169
- * latter is secret-key gated; see `secretKey`). The `tokens` and `swaps`
170
- * namespaces land in v0.2.
222
+ * Today this ships the `auth` namespace, `ephemeralSigners` (secret-key
223
+ * gated; see `secretKey`), and `funding` (the MoonPay onramp sign surface).
224
+ * The `tokens` and `swaps` namespaces land in a later release.
171
225
  */
172
226
  declare class MoonXClient {
173
227
  readonly auth: Auth;
174
228
  readonly ephemeralSigners: EphemeralSigners;
229
+ readonly funding: Funding;
175
230
  constructor(input: MoonXClientConfig);
176
231
  }
177
232
 
178
- type MoonXErrorCode = "malformed_token" | "invalid_signature" | "expired_token" | "audience_mismatch" | "issuer_mismatch" | "subject_mismatch" | "kid_mismatch" | "unsupported_algorithm" | "jwks_fetch_failed" | "app_resolution_failed" | "configuration_error";
233
+ type MoonXErrorCode = "malformed_token" | "invalid_signature" | "expired_token" | "audience_mismatch" | "issuer_mismatch" | "subject_mismatch" | "kid_mismatch" | "unsupported_algorithm" | "jwks_fetch_failed" | "app_resolution_failed" | "configuration_error" | "integration_not_configured" | "wallet_ownership" | "rate_limited" | "unauthorized" | "request_failed";
179
234
  declare class MoonXError extends Error {
180
235
  readonly code: MoonXErrorCode;
181
236
  constructor(code: MoonXErrorCode, message: string);
@@ -213,5 +268,21 @@ declare class AppResolutionError extends MoonXError {
213
268
  declare class ConfigurationError extends MoonXError {
214
269
  constructor(message: string);
215
270
  }
271
+ declare class IntegrationNotConfiguredError extends MoonXError {
272
+ constructor(message?: string);
273
+ }
274
+ declare class WalletOwnershipError extends MoonXError {
275
+ constructor(message?: string);
276
+ }
277
+ declare class RateLimitedError extends MoonXError {
278
+ constructor(message?: string);
279
+ }
280
+ declare class UnauthorizedError extends MoonXError {
281
+ constructor(message?: string);
282
+ }
283
+ declare class RequestFailedError extends MoonXError {
284
+ readonly status: number;
285
+ constructor(status: number, message: string);
286
+ }
216
287
 
217
- export { AppResolutionError, AudienceMismatchError, ConfigurationError, EphemeralSigners, ExpiredTokenError, InvalidTokenError, IssuerMismatchError, JwksFetchError, KidMismatchError, MalformedTokenError, MoonXClient, type MoonXClientConfig, MoonXError, type MoonXErrorCode, type SignEphemeralSignerParams, SubjectMismatchError, UnsupportedAlgorithmError };
288
+ export { AppResolutionError, AudienceMismatchError, ConfigurationError, EphemeralSigners, ExpiredTokenError, Funding, type FundingConfig, IntegrationNotConfiguredError, InvalidTokenError, IssuerMismatchError, JwksFetchError, KidMismatchError, MalformedTokenError, type MoonPaySignParams, type MoonPaySignUiConfig, MoonXClient, type MoonXClientConfig, MoonXError, type MoonXErrorCode, RateLimitedError, RequestFailedError, type SignEphemeralSignerParams, SubjectMismatchError, UnauthorizedError, UnsupportedAlgorithmError, WalletOwnershipError };
package/dist/index.js CHANGED
@@ -26,6 +26,8 @@ __export(src_exports, {
26
26
  EphemeralSignerSignError: () => import_sdk3.EphemeralSignerSignError,
27
27
  EphemeralSigners: () => EphemeralSigners,
28
28
  ExpiredTokenError: () => ExpiredTokenError,
29
+ Funding: () => Funding,
30
+ IntegrationNotConfiguredError: () => IntegrationNotConfiguredError,
29
31
  InvalidTokenError: () => InvalidTokenError,
30
32
  IssuerMismatchError: () => IssuerMismatchError,
31
33
  JwksFetchError: () => JwksFetchError,
@@ -33,8 +35,12 @@ __export(src_exports, {
33
35
  MalformedTokenError: () => MalformedTokenError,
34
36
  MoonXClient: () => MoonXClient,
35
37
  MoonXError: () => MoonXError,
38
+ RateLimitedError: () => RateLimitedError,
39
+ RequestFailedError: () => RequestFailedError,
36
40
  SubjectMismatchError: () => SubjectMismatchError,
37
- UnsupportedAlgorithmError: () => UnsupportedAlgorithmError
41
+ UnauthorizedError: () => UnauthorizedError,
42
+ UnsupportedAlgorithmError: () => UnsupportedAlgorithmError,
43
+ WalletOwnershipError: () => WalletOwnershipError
38
44
  });
39
45
  module.exports = __toCommonJS(src_exports);
40
46
 
@@ -127,6 +133,42 @@ var ConfigurationError = class extends MoonXError {
127
133
  Object.setPrototypeOf(this, new.target.prototype);
128
134
  }
129
135
  };
136
+ var IntegrationNotConfiguredError = class extends MoonXError {
137
+ constructor(message = "MoonPay funding is not configured or is disabled for this app") {
138
+ super("integration_not_configured", message);
139
+ this.name = "IntegrationNotConfiguredError";
140
+ Object.setPrototypeOf(this, new.target.prototype);
141
+ }
142
+ };
143
+ var WalletOwnershipError = class extends MoonXError {
144
+ constructor(message = "wallet address does not belong to this user") {
145
+ super("wallet_ownership", message);
146
+ this.name = "WalletOwnershipError";
147
+ Object.setPrototypeOf(this, new.target.prototype);
148
+ }
149
+ };
150
+ var RateLimitedError = class extends MoonXError {
151
+ constructor(message = "request was rate limited") {
152
+ super("rate_limited", message);
153
+ this.name = "RateLimitedError";
154
+ Object.setPrototypeOf(this, new.target.prototype);
155
+ }
156
+ };
157
+ var UnauthorizedError = class extends MoonXError {
158
+ constructor(message = "missing or invalid credentials") {
159
+ super("unauthorized", message);
160
+ this.name = "UnauthorizedError";
161
+ Object.setPrototypeOf(this, new.target.prototype);
162
+ }
163
+ };
164
+ var RequestFailedError = class extends MoonXError {
165
+ constructor(status, message) {
166
+ super("request_failed", message);
167
+ this.name = "RequestFailedError";
168
+ this.status = status;
169
+ Object.setPrototypeOf(this, new.target.prototype);
170
+ }
171
+ };
130
172
 
131
173
  // src/auth/app-resolver.ts
132
174
  var AppResolver = class {
@@ -529,6 +571,111 @@ var EphemeralSigners = class {
529
571
  }
530
572
  };
531
573
 
574
+ // src/funding/sign.ts
575
+ async function signMoonPay(args) {
576
+ if (!args.accessToken || !args.accessToken.trim()) {
577
+ throw new ConfigurationError("funding.sign: accessToken is required");
578
+ }
579
+ if (!args.publicAddress || !args.publicAddress.trim()) {
580
+ throw new ConfigurationError("funding.sign: publicAddress is required");
581
+ }
582
+ if (args.uiConfig?.theme !== void 0 && args.uiConfig.theme !== "light" && args.uiConfig.theme !== "dark") {
583
+ throw new ConfigurationError(
584
+ 'funding.sign: uiConfig.theme must be "light" or "dark"'
585
+ );
586
+ }
587
+ const fetcher = args.fetchImpl ?? globalThis.fetch;
588
+ const url = `${args.baseUrl.replace(/\/$/, "")}/v0/sdk/onramps/public/moonpay/sign`;
589
+ const config = {};
590
+ if (args.currencyCode !== void 0) config.currency_code = args.currencyCode;
591
+ if (args.quoteCurrencyAmount !== void 0) {
592
+ config.quote_currency_amount = args.quoteCurrencyAmount;
593
+ }
594
+ if (args.paymentMethod !== void 0) {
595
+ config.payment_method = args.paymentMethod;
596
+ }
597
+ if (args.uiConfig) {
598
+ const uiConfig = {};
599
+ if (args.uiConfig.theme !== void 0) uiConfig.theme = args.uiConfig.theme;
600
+ if (args.uiConfig.accentColor !== void 0) {
601
+ uiConfig.accent_color = args.uiConfig.accentColor;
602
+ }
603
+ if (Object.keys(uiConfig).length > 0) config.ui_config = uiConfig;
604
+ }
605
+ const body = {
606
+ public_address: args.publicAddress,
607
+ use_sandbox: args.useSandbox ?? false
608
+ };
609
+ if (Object.keys(config).length > 0) body.config = config;
610
+ let res;
611
+ try {
612
+ res = await fetcher(url, {
613
+ method: "POST",
614
+ headers: {
615
+ "Content-Type": "application/json",
616
+ Authorization: `PublicToken ${args.publishableKey}`,
617
+ "X-MoonX-Access-Token": args.accessToken
618
+ },
619
+ body: JSON.stringify(body),
620
+ signal: AbortSignal.timeout(1e4)
621
+ });
622
+ } catch (err) {
623
+ throw new RequestFailedError(
624
+ 0,
625
+ `moonpay sign request failed: ${err instanceof Error ? err.message : String(err)}`
626
+ );
627
+ }
628
+ if (!res.ok) {
629
+ const message = await errorMessage(res);
630
+ switch (res.status) {
631
+ case 400:
632
+ throw new IntegrationNotConfiguredError(message || void 0);
633
+ case 401:
634
+ throw new UnauthorizedError(message || void 0);
635
+ case 403:
636
+ throw new WalletOwnershipError(message || void 0);
637
+ case 429:
638
+ throw new RateLimitedError(message || void 0);
639
+ default:
640
+ throw new RequestFailedError(
641
+ res.status,
642
+ `moonpay sign failed: HTTP ${res.status}${message ? `: ${message}` : ""}`
643
+ );
644
+ }
645
+ }
646
+ return await res.json();
647
+ }
648
+ async function errorMessage(res) {
649
+ try {
650
+ const body = await res.json();
651
+ return body.error ?? body.message ?? "";
652
+ } catch {
653
+ return "";
654
+ }
655
+ }
656
+
657
+ // src/funding/index.ts
658
+ var Funding = class {
659
+ constructor(cfg) {
660
+ this.cfg = cfg;
661
+ }
662
+ /**
663
+ * Request a signed MoonPay Buy-widget URL for the user's wallet. Returns the
664
+ * `signed_url` to open (plus the transaction id and provider publishable key
665
+ * used to track the purchase). Throws a typed {@link MoonXError} subclass on
666
+ * failure: IntegrationNotConfiguredError (400), UnauthorizedError (401),
667
+ * WalletOwnershipError (403), RateLimitedError (429).
668
+ */
669
+ async sign(params) {
670
+ return signMoonPay({
671
+ ...params,
672
+ baseUrl: this.cfg.baseUrl,
673
+ publishableKey: this.cfg.publishableKey,
674
+ fetchImpl: this.cfg.fetchImpl
675
+ });
676
+ }
677
+ };
678
+
532
679
  // src/client.ts
533
680
  var MoonXClient = class {
534
681
  constructor(input) {
@@ -546,6 +693,11 @@ var MoonXClient = class {
546
693
  secretKey: cfg.secretKey,
547
694
  fetchImpl: cfg.fetch
548
695
  });
696
+ this.funding = new Funding({
697
+ baseUrl: cfg.baseUrl,
698
+ publishableKey: cfg.publishableKey,
699
+ fetchImpl: cfg.fetch
700
+ });
549
701
  }
550
702
  };
551
703
 
@@ -559,6 +711,8 @@ var import_sdk3 = require("@moon-x/core/sdk");
559
711
  EphemeralSignerSignError,
560
712
  EphemeralSigners,
561
713
  ExpiredTokenError,
714
+ Funding,
715
+ IntegrationNotConfiguredError,
562
716
  InvalidTokenError,
563
717
  IssuerMismatchError,
564
718
  JwksFetchError,
@@ -566,6 +720,10 @@ var import_sdk3 = require("@moon-x/core/sdk");
566
720
  MalformedTokenError,
567
721
  MoonXClient,
568
722
  MoonXError,
723
+ RateLimitedError,
724
+ RequestFailedError,
569
725
  SubjectMismatchError,
570
- UnsupportedAlgorithmError
726
+ UnauthorizedError,
727
+ UnsupportedAlgorithmError,
728
+ WalletOwnershipError
571
729
  });
package/dist/index.mjs CHANGED
@@ -87,6 +87,42 @@ var ConfigurationError = class extends MoonXError {
87
87
  Object.setPrototypeOf(this, new.target.prototype);
88
88
  }
89
89
  };
90
+ var IntegrationNotConfiguredError = class extends MoonXError {
91
+ constructor(message = "MoonPay funding is not configured or is disabled for this app") {
92
+ super("integration_not_configured", message);
93
+ this.name = "IntegrationNotConfiguredError";
94
+ Object.setPrototypeOf(this, new.target.prototype);
95
+ }
96
+ };
97
+ var WalletOwnershipError = class extends MoonXError {
98
+ constructor(message = "wallet address does not belong to this user") {
99
+ super("wallet_ownership", message);
100
+ this.name = "WalletOwnershipError";
101
+ Object.setPrototypeOf(this, new.target.prototype);
102
+ }
103
+ };
104
+ var RateLimitedError = class extends MoonXError {
105
+ constructor(message = "request was rate limited") {
106
+ super("rate_limited", message);
107
+ this.name = "RateLimitedError";
108
+ Object.setPrototypeOf(this, new.target.prototype);
109
+ }
110
+ };
111
+ var UnauthorizedError = class extends MoonXError {
112
+ constructor(message = "missing or invalid credentials") {
113
+ super("unauthorized", message);
114
+ this.name = "UnauthorizedError";
115
+ Object.setPrototypeOf(this, new.target.prototype);
116
+ }
117
+ };
118
+ var RequestFailedError = class extends MoonXError {
119
+ constructor(status, message) {
120
+ super("request_failed", message);
121
+ this.name = "RequestFailedError";
122
+ this.status = status;
123
+ Object.setPrototypeOf(this, new.target.prototype);
124
+ }
125
+ };
90
126
 
91
127
  // src/auth/app-resolver.ts
92
128
  var AppResolver = class {
@@ -491,6 +527,111 @@ var EphemeralSigners = class {
491
527
  }
492
528
  };
493
529
 
530
+ // src/funding/sign.ts
531
+ async function signMoonPay(args) {
532
+ if (!args.accessToken || !args.accessToken.trim()) {
533
+ throw new ConfigurationError("funding.sign: accessToken is required");
534
+ }
535
+ if (!args.publicAddress || !args.publicAddress.trim()) {
536
+ throw new ConfigurationError("funding.sign: publicAddress is required");
537
+ }
538
+ if (args.uiConfig?.theme !== void 0 && args.uiConfig.theme !== "light" && args.uiConfig.theme !== "dark") {
539
+ throw new ConfigurationError(
540
+ 'funding.sign: uiConfig.theme must be "light" or "dark"'
541
+ );
542
+ }
543
+ const fetcher = args.fetchImpl ?? globalThis.fetch;
544
+ const url = `${args.baseUrl.replace(/\/$/, "")}/v0/sdk/onramps/public/moonpay/sign`;
545
+ const config = {};
546
+ if (args.currencyCode !== void 0) config.currency_code = args.currencyCode;
547
+ if (args.quoteCurrencyAmount !== void 0) {
548
+ config.quote_currency_amount = args.quoteCurrencyAmount;
549
+ }
550
+ if (args.paymentMethod !== void 0) {
551
+ config.payment_method = args.paymentMethod;
552
+ }
553
+ if (args.uiConfig) {
554
+ const uiConfig = {};
555
+ if (args.uiConfig.theme !== void 0) uiConfig.theme = args.uiConfig.theme;
556
+ if (args.uiConfig.accentColor !== void 0) {
557
+ uiConfig.accent_color = args.uiConfig.accentColor;
558
+ }
559
+ if (Object.keys(uiConfig).length > 0) config.ui_config = uiConfig;
560
+ }
561
+ const body = {
562
+ public_address: args.publicAddress,
563
+ use_sandbox: args.useSandbox ?? false
564
+ };
565
+ if (Object.keys(config).length > 0) body.config = config;
566
+ let res;
567
+ try {
568
+ res = await fetcher(url, {
569
+ method: "POST",
570
+ headers: {
571
+ "Content-Type": "application/json",
572
+ Authorization: `PublicToken ${args.publishableKey}`,
573
+ "X-MoonX-Access-Token": args.accessToken
574
+ },
575
+ body: JSON.stringify(body),
576
+ signal: AbortSignal.timeout(1e4)
577
+ });
578
+ } catch (err) {
579
+ throw new RequestFailedError(
580
+ 0,
581
+ `moonpay sign request failed: ${err instanceof Error ? err.message : String(err)}`
582
+ );
583
+ }
584
+ if (!res.ok) {
585
+ const message = await errorMessage(res);
586
+ switch (res.status) {
587
+ case 400:
588
+ throw new IntegrationNotConfiguredError(message || void 0);
589
+ case 401:
590
+ throw new UnauthorizedError(message || void 0);
591
+ case 403:
592
+ throw new WalletOwnershipError(message || void 0);
593
+ case 429:
594
+ throw new RateLimitedError(message || void 0);
595
+ default:
596
+ throw new RequestFailedError(
597
+ res.status,
598
+ `moonpay sign failed: HTTP ${res.status}${message ? `: ${message}` : ""}`
599
+ );
600
+ }
601
+ }
602
+ return await res.json();
603
+ }
604
+ async function errorMessage(res) {
605
+ try {
606
+ const body = await res.json();
607
+ return body.error ?? body.message ?? "";
608
+ } catch {
609
+ return "";
610
+ }
611
+ }
612
+
613
+ // src/funding/index.ts
614
+ var Funding = class {
615
+ constructor(cfg) {
616
+ this.cfg = cfg;
617
+ }
618
+ /**
619
+ * Request a signed MoonPay Buy-widget URL for the user's wallet. Returns the
620
+ * `signed_url` to open (plus the transaction id and provider publishable key
621
+ * used to track the purchase). Throws a typed {@link MoonXError} subclass on
622
+ * failure: IntegrationNotConfiguredError (400), UnauthorizedError (401),
623
+ * WalletOwnershipError (403), RateLimitedError (429).
624
+ */
625
+ async sign(params) {
626
+ return signMoonPay({
627
+ ...params,
628
+ baseUrl: this.cfg.baseUrl,
629
+ publishableKey: this.cfg.publishableKey,
630
+ fetchImpl: this.cfg.fetchImpl
631
+ });
632
+ }
633
+ };
634
+
494
635
  // src/client.ts
495
636
  var MoonXClient = class {
496
637
  constructor(input) {
@@ -508,6 +649,11 @@ var MoonXClient = class {
508
649
  secretKey: cfg.secretKey,
509
650
  fetchImpl: cfg.fetch
510
651
  });
652
+ this.funding = new Funding({
653
+ baseUrl: cfg.baseUrl,
654
+ publishableKey: cfg.publishableKey,
655
+ fetchImpl: cfg.fetch
656
+ });
511
657
  }
512
658
  };
513
659
 
@@ -522,6 +668,8 @@ export {
522
668
  EphemeralSignerSignError,
523
669
  EphemeralSigners,
524
670
  ExpiredTokenError,
671
+ Funding,
672
+ IntegrationNotConfiguredError,
525
673
  InvalidTokenError,
526
674
  IssuerMismatchError,
527
675
  JwksFetchError,
@@ -529,6 +677,10 @@ export {
529
677
  MalformedTokenError,
530
678
  MoonXClient,
531
679
  MoonXError,
680
+ RateLimitedError,
681
+ RequestFailedError,
532
682
  SubjectMismatchError,
533
- UnsupportedAlgorithmError
683
+ UnauthorizedError,
684
+ UnsupportedAlgorithmError,
685
+ WalletOwnershipError
534
686
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moon-x/node-sdk",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "MoonX server-side SDK for Node.js. Verify MoonX-issued access + identity tokens with zero runtime dependencies.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -30,7 +30,7 @@
30
30
  "access": "public"
31
31
  },
32
32
  "dependencies": {
33
- "@moon-x/core": "0.11.0"
33
+ "@moon-x/core": "0.14.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "tsup": "8.5.1",