@ardrive/turbo-sdk 1.42.0-alpha.6 → 1.42.0-alpha.8

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.
@@ -102,6 +102,11 @@ const utils_js_1 = require("./utils.js");
102
102
  .description('Get the Turbo Credit (winc + mARIO) price to buy, extend, increase undernames, or upgrade an ArNS name'), options_js_1.arnsPriceOptions).action(async (_commandOptions, command) => {
103
103
  await (0, utils_js_1.runCommand)(command, arns_js_1.arnsPrice);
104
104
  });
105
+ (0, utils_js_1.applyOptions)(commander_1.program
106
+ .command('arns-fiat-quote')
107
+ .description('Quote an ArNS purchase paid by credit card (Stripe) - no credits needed'), options_js_1.arnsFiatQuoteOptions).action(async (_commandOptions, command) => {
108
+ await (0, utils_js_1.runCommand)(command, arns_js_1.arnsFiatQuote);
109
+ });
105
110
  (0, utils_js_1.applyOptions)(commander_1.program
106
111
  .command('buy-arns-name')
107
112
  .description('Buy an ArNS name (lease or permabuy) with Turbo Credits'), options_js_1.buyArNSNameOptions).action(async (_commandOptions, command) => {
@@ -6,6 +6,7 @@ exports.typeFromOptions = typeFromOptions;
6
6
  exports.paidByFromArNSOptions = paidByFromArNSOptions;
7
7
  exports.arnsPriceParamsFromOptions = arnsPriceParamsFromOptions;
8
8
  exports.arnsPrice = arnsPrice;
9
+ exports.arnsFiatQuote = arnsFiatQuote;
9
10
  exports.buyArNSName = buyArNSName;
10
11
  exports.extendArNSLease = extendArNSLease;
11
12
  exports.increaseArNSUndernames = increaseArNSUndernames;
@@ -146,6 +147,53 @@ async function arnsPrice(options, turbo) {
146
147
  mARIO,
147
148
  }, null, 2));
148
149
  }
150
+ async function arnsFiatQuote(options, turbo) {
151
+ const params = arnsPriceParamsFromOptions(options);
152
+ // `arnsPriceParamsFromOptions` substitutes PRICING_PLACEHOLDER_PROCESS_ID for
153
+ // an omitted Buy-Name `processId`, which is fine for a price lookup but NOT
154
+ // here: a quote records a real purchase, and that placeholder is not a valid
155
+ // ANT. Send `processId` only when the caller actually supplied one — omitting
156
+ // it is what tells Turbo to custodially provision the ANT.
157
+ if (options.processId === undefined) {
158
+ delete params.processId;
159
+ }
160
+ const address = options.address;
161
+ if (address === undefined) {
162
+ throw new Error('A destination --address is required for a fiat ArNS quote.');
163
+ }
164
+ const client = turbo ?? factory_js_1.TurboFactory.unauthenticated((0, utils_js_1.configFromOptions)(options));
165
+ try {
166
+ const { purchaseQuote, paymentSession, adjustments, fees } = await client.getArNSFiatPurchaseQuote({
167
+ ...params,
168
+ address,
169
+ currency: (options.currency ?? 'usd'),
170
+ method: options.method,
171
+ promoCodes: options.promoCode,
172
+ });
173
+ console.log(JSON.stringify({
174
+ name: purchaseQuote.name,
175
+ intent: purchaseQuote.intent,
176
+ nonce: purchaseQuote.nonce,
177
+ paymentAmount: purchaseQuote.paymentAmount,
178
+ currency: purchaseQuote.currencyType,
179
+ quoteExpirationDate: purchaseQuote.quoteExpirationDate,
180
+ paymentSessionId: paymentSession.id,
181
+ // Present for a payment intent / embedded checkout; a hosted
182
+ // checkout session returns `url` instead.
183
+ clientSecret: paymentSession.client_secret ?? undefined,
184
+ checkoutUrl: paymentSession.url ?? undefined,
185
+ adjustments,
186
+ fees,
187
+ }, null, 2));
188
+ }
189
+ catch (error) {
190
+ if (error instanceof errors_js_1.FiatPaymentsDisabledError) {
191
+ console.error('Fiat (Stripe) payments are disabled on this payment service. Use the credit-paid commands (buy-arns-name, extend-arns-lease, ...) instead.');
192
+ throw error;
193
+ }
194
+ throw error;
195
+ }
196
+ }
149
197
  async function buyArNSName(options, turbo) {
150
198
  const name = requiredNameFromOptions(options);
151
199
  const type = typeFromOptions(options.type);
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.removeArNSRecordOptions = exports.setArNSRecordOptions = exports.transferArNSAntOptions = exports.arnsPurchaseStatusOptions = exports.upgradeArNSNameOptions = exports.increaseArNSUndernamesOptions = exports.extendArNSLeaseOptions = exports.buyArNSNameOptions = exports.arnsPriceOptions = exports.listSharesOptions = exports.revokeCreditsOptions = exports.shareCreditsOptions = exports.uploadFileOptions = exports.uploadFolderOptions = exports.uploadOptions = exports.globalOptions = exports.walletOptions = exports.optionMap = void 0;
18
+ exports.removeArNSRecordOptions = exports.setArNSRecordOptions = exports.transferArNSAntOptions = exports.arnsPurchaseStatusOptions = exports.upgradeArNSNameOptions = exports.increaseArNSUndernamesOptions = exports.extendArNSLeaseOptions = exports.buyArNSNameOptions = exports.arnsFiatQuoteOptions = exports.arnsPriceOptions = exports.listSharesOptions = exports.revokeCreditsOptions = exports.shareCreditsOptions = exports.uploadFileOptions = exports.uploadFolderOptions = exports.uploadOptions = exports.globalOptions = exports.walletOptions = exports.optionMap = void 0;
19
19
  exports.optionMap = {
20
20
  token: {
21
21
  alias: '-t, --token <type>',
@@ -304,6 +304,23 @@ exports.arnsPriceOptions = [
304
304
  exports.optionMap.arnsIncreaseQty,
305
305
  exports.optionMap.arnsProcessId,
306
306
  ];
307
+ exports.arnsFiatQuoteOptions = [
308
+ exports.optionMap.arnsName,
309
+ exports.optionMap.arnsType,
310
+ exports.optionMap.arnsYears,
311
+ exports.optionMap.arnsIncreaseQty,
312
+ exports.optionMap.arnsProcessId,
313
+ exports.optionMap.address,
314
+ exports.optionMap.currency,
315
+ {
316
+ alias: '--method <method>',
317
+ description: 'Stripe integration mode: payment-intent (default) or checkout-session',
318
+ },
319
+ {
320
+ alias: '--promo-code <promoCode...>',
321
+ description: 'Promo code(s) to apply to the fiat price',
322
+ },
323
+ ];
307
324
  exports.buyArNSNameOptions = [
308
325
  ...exports.walletOptions,
309
326
  exports.optionMap.arnsName,
@@ -62,6 +62,22 @@ class TurboUnauthenticatedPaymentService {
62
62
  // and coerce a missing field (e.g. a 404 body) to `null`.
63
63
  return { bytesRemaining: status?.bytesRemaining ?? null };
64
64
  }
65
+ /**
66
+ * Returns the ArNS names a wallet owns or controls -- both custodial names
67
+ * bought via Turbo's ArNS-with-credits feature (Turbo may spawn and hold
68
+ * the ANT on the caller's behalf, depending on the buy) and self-custody
69
+ * names, in one list. See `TurboArNSName` for field semantics, including
70
+ * the `custodial` flag distinguishing the two.
71
+ *
72
+ * To read a name's current records or lease/expiration state, use
73
+ * `@ar.io/sdk` directly against the returned `antId` -- it talks to the
74
+ * chain directly and needs no round-trip through this SDK/backend.
75
+ */
76
+ getArNSNames(address) {
77
+ return this.httpService.get({
78
+ endpoint: `/arns/my-names/${encodeURIComponent(address)}`,
79
+ });
80
+ }
65
81
  getFiatRates() {
66
82
  return this.httpService.get({
67
83
  endpoint: '/rates',
@@ -190,6 +206,95 @@ class TurboUnauthenticatedPaymentService {
190
206
  const query = params.toString();
191
207
  return query.length > 0 ? `?${query}` : '';
192
208
  }
209
+ /**
210
+ * Quote a fiat (Stripe) ArNS purchase — buy a name with a credit card in one
211
+ * step, with no Turbo Credits top-up in between.
212
+ *
213
+ * Returns the recorded `purchaseQuote` (its `nonce` is what
214
+ * `getArNSPurchaseStatus` polls) plus the Stripe `paymentSession` to complete
215
+ * payment with. For `payment-intent`, confirm client-side with
216
+ * `stripe.confirmCardPayment(paymentSession.client_secret, ...)`, then poll
217
+ * the nonce until the purchase reports success or failure.
218
+ *
219
+ * Throws {@link FiatPaymentsDisabledError} when the service has Stripe turned
220
+ * off (normal in the testnet sandbox) so callers can fall back to the
221
+ * credit-paid path without string-matching a generic 503.
222
+ */
223
+ async getArNSFiatPurchaseQuote(params) {
224
+ this.validateArNSPurchaseParams(params);
225
+ const { address, currency, method = 'payment-intent', promoCodes = [], } = params;
226
+ if (typeof address !== 'string' || address.length === 0) {
227
+ throw new errors_js_1.ProvidedInputError('A destination `address` is required for a fiat ArNS purchase quote.');
228
+ }
229
+ if (!(0, types_js_1.isCurrency)(currency)) {
230
+ throw new errors_js_1.ProvidedInputError(`Invalid currency '${currency}'. Supported: ${types_js_1.fiatCurrencyTypes.join(', ')}`);
231
+ }
232
+ // Every interpolated segment is encoded. Five user-controlled values land in
233
+ // the path here, and an unencoded one (e.g. a name or address containing
234
+ // `../`) would silently retarget the request at another route.
235
+ const segments = [
236
+ method,
237
+ address,
238
+ currency,
239
+ params.intent,
240
+ params.name,
241
+ ].map((segment) => encodeURIComponent(segment));
242
+ const query = this.buildArNSFiatQuoteQuery(params, promoCodes);
243
+ try {
244
+ return await this.httpService.get({
245
+ endpoint: `/arns/quote/${segments.join('/')}${query}`,
246
+ });
247
+ }
248
+ catch (error) {
249
+ // The service returns 503 both for "Stripe is disabled" and for internal
250
+ // errors, so the body is what disambiguates them.
251
+ if (error instanceof errors_js_1.FailedRequestError &&
252
+ error.status === 503 &&
253
+ /Fiat \(Stripe\).*disabled/i.test(error.message)) {
254
+ throw new errors_js_1.FiatPaymentsDisabledError(error.message);
255
+ }
256
+ throw error;
257
+ }
258
+ }
259
+ /**
260
+ * Query string for a fiat quote. Distinct from `buildArNSPurchaseQuery`
261
+ * because this route takes `uiMode` + its paired URLs and has no `paidBy`
262
+ * (fiat has no delegated payer), and because promo codes must be REPEATED
263
+ * params here: the service reads them with `parseQueryParams`, which treats a
264
+ * comma-joined string as one code rather than several.
265
+ */
266
+ buildArNSFiatQuoteQuery(params, promoCodes) {
267
+ const { type, years, increaseQty, processId } = params;
268
+ const search = new URLSearchParams();
269
+ if (type !== undefined)
270
+ search.set('type', type);
271
+ if (years !== undefined)
272
+ search.set('years', `${years}`);
273
+ if (increaseQty !== undefined)
274
+ search.set('increaseQty', `${increaseQty}`);
275
+ if (processId !== undefined)
276
+ search.set('processId', processId);
277
+ const uiMode = params.uiMode;
278
+ if (uiMode !== undefined)
279
+ search.set('uiMode', uiMode);
280
+ if (uiMode === 'embedded') {
281
+ const { returnUrl } = params;
282
+ if (returnUrl !== undefined)
283
+ search.set('returnUrl', returnUrl);
284
+ }
285
+ else {
286
+ const { successUrl, cancelUrl } = params;
287
+ if (successUrl !== undefined)
288
+ search.set('successUrl', successUrl);
289
+ if (cancelUrl !== undefined)
290
+ search.set('cancelUrl', cancelUrl);
291
+ }
292
+ for (const code of promoCodes) {
293
+ search.append('promoCode', code);
294
+ }
295
+ const query = search.toString();
296
+ return query.length > 0 ? `?${query}` : '';
297
+ }
193
298
  appendPromoCodesToQuery(promoCodes) {
194
299
  const promoCodesQuery = promoCodes.join(',');
195
300
  return promoCodesQuery ? `promoCode=${promoCodesQuery}` : '';
@@ -355,6 +460,20 @@ class TurboAuthenticatedPaymentService extends TurboUnauthenticatedPaymentServic
355
460
  userAddress ??= await this.signer.getNativeAddress();
356
461
  return super.getBalance(userAddress);
357
462
  }
463
+ /**
464
+ * Quote a fiat (Stripe) ArNS purchase. `address` defaults to this signer's
465
+ * native address — the wallet that will own the name — so the common case
466
+ * needs no address at all. Pass one explicitly to buy on another wallet's
467
+ * behalf; the route takes the destination as a path param and requires no
468
+ * signature, which is why it is available unauthenticated too.
469
+ */
470
+ async getArNSFiatPurchaseQuote(params) {
471
+ const address = params.address ?? (await this.signer.getNativeAddress());
472
+ return super.getArNSFiatPurchaseQuote({
473
+ ...params,
474
+ address,
475
+ });
476
+ }
358
477
  async getFreeStatus(userAddress) {
359
478
  userAddress ??= await this.signer.getNativeAddress();
360
479
  return super.getFreeStatus(userAddress);
@@ -498,6 +617,15 @@ class TurboAuthenticatedPaymentService extends TurboUnauthenticatedPaymentServic
498
617
  retry: false, // single-use action-bound nonce; don't re-POST on 5xx
499
618
  });
500
619
  }
620
+ /**
621
+ * Defaults to the signer's own address when `userAddress` is omitted
622
+ * (`null`/`undefined`). Passing `''` does NOT trigger this default --
623
+ * mirrors `getBalance`'s existing behavior above.
624
+ */
625
+ async getArNSNames(userAddress) {
626
+ userAddress ??= await this.signer.getNativeAddress();
627
+ return super.getArNSNames(userAddress);
628
+ }
501
629
  async getCreditShareApprovals({ userAddress, }) {
502
630
  userAddress ??= await this.signer.getNativeAddress();
503
631
  return super.getCreditShareApprovals({ userAddress });
@@ -69,6 +69,30 @@ class TurboUnauthenticatedClient {
69
69
  getArNSPurchaseStatus(p) {
70
70
  return this.paymentService.getArNSPurchaseStatus(p);
71
71
  }
72
+ /**
73
+ * Returns the ArNS names a wallet owns or controls via Turbo's custodial
74
+ * ArNS-with-credits feature. `custodial: true` on a returned name means
75
+ * Turbo still holds/manages its ANT (transfer/manage routes apply);
76
+ * `custodial: false` means self-custody (or an already-completed exit) and
77
+ * is informational only.
78
+ *
79
+ * To read a name's current records or lease/expiration state, use
80
+ * `@ar.io/sdk` directly against the returned `antId`.
81
+ */
82
+ getArNSNames(address) {
83
+ return this.paymentService.getArNSNames(address);
84
+ }
85
+ /**
86
+ * Quote a fiat (Stripe) ArNS purchase — buy a name with a credit card in one
87
+ * step, no Turbo Credits top-up in between. Complete the returned
88
+ * `paymentSession` with Stripe, then poll {@link getArNSPurchaseStatus} using
89
+ * `purchaseQuote.nonce`.
90
+ *
91
+ * Throws `FiatPaymentsDisabledError` when the service has Stripe switched off.
92
+ */
93
+ getArNSFiatPurchaseQuote(params) {
94
+ return this.paymentService.getArNSFiatPurchaseQuote(params);
95
+ }
72
96
  /**
73
97
  * Returns a list of all supported fiat currencies.
74
98
  */
@@ -201,6 +225,18 @@ class TurboAuthenticatedClient extends TurboUnauthenticatedClient {
201
225
  buyArNSName(params) {
202
226
  return this.paymentService.buyArNSName(params);
203
227
  }
228
+ /**
229
+ * Quote a fiat (Stripe) ArNS purchase for this signer's wallet — a credit-card
230
+ * buy in one step, with no Turbo Credits top-up in between. `address` defaults
231
+ * to the signer's native address; pass one to buy on another wallet's behalf.
232
+ *
233
+ * Complete the returned `paymentSession` with Stripe, then poll
234
+ * {@link getArNSPurchaseStatus} using `purchaseQuote.nonce`. Throws
235
+ * `FiatPaymentsDisabledError` when the service has Stripe switched off.
236
+ */
237
+ getArNSFiatPurchaseQuote(params) {
238
+ return this.paymentService.getArNSFiatPurchaseQuote(params);
239
+ }
204
240
  /** Extends the lease on an existing ArNS name. */
205
241
  extendArNSLease(params) {
206
242
  return this.paymentService.extendArNSLease(params);
@@ -222,6 +258,14 @@ class TurboAuthenticatedClient extends TurboUnauthenticatedClient {
222
258
  removeArNSRecord(params) {
223
259
  return this.paymentService.removeArNSRecord(params);
224
260
  }
261
+ /**
262
+ * Returns the ArNS names owned or controlled by the connected signer's
263
+ * wallet (or the given `userAddress`) via Turbo's custodial
264
+ * ArNS-with-credits feature.
265
+ */
266
+ getArNSNames(userAddress) {
267
+ return this.paymentService.getArNSNames(userAddress);
268
+ }
225
269
  /**
226
270
  * Returns a list of all credit share approvals for the user.
227
271
  */
package/lib/cjs/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.arNSPurchaseIntents = exports.validChunkingModes = exports.isJWK = exports.isWebUploadFolderParams = exports.isNodeUploadFolderParams = exports.multipartFinalizedStatus = exports.multipartFailedStatus = exports.multipartPendingStatus = exports.X402Funding = exports.OnDemandFunding = exports.ExistingBalanceFunding = exports.supportedEvmSignerTokens = exports.tokenTypes = exports.fiatCurrencyTypes = void 0;
3
+ exports.arNSFiatPurchaseMethods = exports.arNSPurchaseIntents = exports.validChunkingModes = exports.isJWK = exports.isWebUploadFolderParams = exports.isNodeUploadFolderParams = exports.multipartFinalizedStatus = exports.multipartFailedStatus = exports.multipartPendingStatus = exports.X402Funding = exports.OnDemandFunding = exports.ExistingBalanceFunding = exports.supportedEvmSignerTokens = exports.tokenTypes = exports.fiatCurrencyTypes = void 0;
4
4
  exports.isCurrency = isCurrency;
5
5
  exports.isKyvePrivateKey = isKyvePrivateKey;
6
6
  exports.isEthPrivateKey = isEthPrivateKey;
@@ -113,3 +113,20 @@ exports.arNSPurchaseIntents = [
113
113
  'Increase-Undername-Limit',
114
114
  'Upgrade-Name',
115
115
  ];
116
+ // ===== ArNS purchases paid with fiat (Stripe) — no Turbo Credits in between ====
117
+ /**
118
+ * Stripe integration mode for a fiat ArNS purchase quote.
119
+ *
120
+ * - `payment-intent` — returns a Stripe PaymentIntent. Confirm it client-side
121
+ * with `stripe.confirmCardPayment(paymentSession.client_secret, ...)`.
122
+ * - `checkout-session` — returns a Stripe Checkout Session to redirect to
123
+ * (`uiMode: 'hosted'`) or embed (`uiMode: 'embedded'`).
124
+ *
125
+ * Widened with `string & Record<never, never>` so a method added service-side is
126
+ * still callable without an SDK bump, while the known values keep autocomplete.
127
+ * (`string & {}` is the usual idiom but trips the `ban-types` lint rule.)
128
+ */
129
+ exports.arNSFiatPurchaseMethods = [
130
+ 'payment-intent',
131
+ 'checkout-session',
132
+ ];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AbortError = exports.InsufficientCreditsError = exports.ProvidedInputError = exports.FailedRequestError = exports.UnauthenticatedRequestError = exports.BaseError = void 0;
3
+ exports.AbortError = exports.FiatPaymentsDisabledError = exports.InsufficientCreditsError = exports.ProvidedInputError = exports.FailedRequestError = exports.UnauthenticatedRequestError = exports.BaseError = void 0;
4
4
  /**
5
5
  * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
6
6
  *
@@ -60,6 +60,25 @@ class InsufficientCreditsError extends BaseError {
60
60
  }
61
61
  }
62
62
  exports.InsufficientCreditsError = InsufficientCreditsError;
63
+ /**
64
+ * Raised when the payment service has fiat (Stripe) payments switched off, which
65
+ * it signals with `503` and the body "Fiat (Stripe) ArNS payments are disabled".
66
+ * This is a normal state in the testnet sandbox, not an outage.
67
+ *
68
+ * Distinguished from a generic `503` deliberately: the same status is also used
69
+ * for internal errors (body "Internal Server Error: ..."), so status alone is
70
+ * ambiguous. Recovery: fall back to the credit-paid path (`buyArNSName` and
71
+ * friends), or surface fiat checkout as unavailable.
72
+ */
73
+ class FiatPaymentsDisabledError extends BaseError {
74
+ constructor(message) {
75
+ super(message ??
76
+ 'Fiat (Stripe) payments are disabled on this payment service. Use the credit-paid purchase path instead.');
77
+ /** Always the HTTP status that produced this error. */
78
+ this.status = 503;
79
+ }
80
+ }
81
+ exports.FiatPaymentsDisabledError = FiatPaymentsDisabledError;
63
82
  class AbortError extends BaseError {
64
83
  constructor(message = 'Request was aborted') {
65
84
  super(message);
@@ -20,7 +20,7 @@ import { DataItem } from '@dha-team/arbundles';
20
20
  import { program } from 'commander';
21
21
  import { readFileSync, readdirSync } from 'fs';
22
22
  import { version } from '../version.js';
23
- import { arnsPrice, arnsPurchaseStatus, buyArNSName, extendArNSLease, increaseArNSUndernames, removeArNSRecord, setArNSRecord, transferArNSAnt, upgradeArNSName, } from './commands/arns.js';
23
+ import { arnsFiatQuote, arnsPrice, arnsPurchaseStatus, buyArNSName, extendArNSLease, increaseArNSUndernames, removeArNSRecord, setArNSRecord, transferArNSAnt, upgradeArNSName, } from './commands/arns.js';
24
24
  import { fiatEstimate } from './commands/fiatEstimate.js';
25
25
  import { balance, cryptoFund, freeStatus, paymentHistory, price, topUp, uploadFile, uploadFolder, } from './commands/index.js';
26
26
  import { listShares } from './commands/listShares.js';
@@ -28,7 +28,7 @@ import { revokeCredits } from './commands/revokeCredits.js';
28
28
  import { shareCredits } from './commands/shareCredits.js';
29
29
  import { tokenPrice } from './commands/tokenPrice.js';
30
30
  import { x402UploadUnsignedFile } from './commands/x402UploadUnsignedData.js';
31
- import { arnsPriceOptions, arnsPurchaseStatusOptions, buyArNSNameOptions, extendArNSLeaseOptions, globalOptions, increaseArNSUndernamesOptions, listSharesOptions, optionMap, removeArNSRecordOptions, revokeCreditsOptions, setArNSRecordOptions, shareCreditsOptions, transferArNSAntOptions, upgradeArNSNameOptions, uploadFileOptions, uploadFolderOptions, walletOptions, } from './options.js';
31
+ import { arnsFiatQuoteOptions, arnsPriceOptions, arnsPurchaseStatusOptions, buyArNSNameOptions, extendArNSLeaseOptions, globalOptions, increaseArNSUndernamesOptions, listSharesOptions, optionMap, removeArNSRecordOptions, revokeCreditsOptions, setArNSRecordOptions, shareCreditsOptions, transferArNSAntOptions, upgradeArNSNameOptions, uploadFileOptions, uploadFolderOptions, walletOptions, } from './options.js';
32
32
  import { applyOptions, runCommand } from './utils.js';
33
33
  applyOptions(program
34
34
  .name('turbo')
@@ -100,6 +100,11 @@ applyOptions(program
100
100
  .description('Get the Turbo Credit (winc + mARIO) price to buy, extend, increase undernames, or upgrade an ArNS name'), arnsPriceOptions).action(async (_commandOptions, command) => {
101
101
  await runCommand(command, arnsPrice);
102
102
  });
103
+ applyOptions(program
104
+ .command('arns-fiat-quote')
105
+ .description('Quote an ArNS purchase paid by credit card (Stripe) - no credits needed'), arnsFiatQuoteOptions).action(async (_commandOptions, command) => {
106
+ await runCommand(command, arnsFiatQuote);
107
+ });
103
108
  applyOptions(program
104
109
  .command('buy-arns-name')
105
110
  .description('Buy an ArNS name (lease or permabuy) with Turbo Credits'), buyArNSNameOptions).action(async (_commandOptions, command) => {
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { BigNumber } from 'bignumber.js';
17
17
  import { TurboFactory } from '../../node/factory.js';
18
- import { InsufficientCreditsError } from '../../utils/errors.js';
18
+ import { FiatPaymentsDisabledError, InsufficientCreditsError, } from '../../utils/errors.js';
19
19
  import { wincPerCredit } from '../constants.js';
20
20
  import { configFromOptions, turboFromOptions } from '../utils.js';
21
21
  export function requiredNameFromOptions(options) {
@@ -130,6 +130,53 @@ export async function arnsPrice(options, turbo) {
130
130
  mARIO,
131
131
  }, null, 2));
132
132
  }
133
+ export async function arnsFiatQuote(options, turbo) {
134
+ const params = arnsPriceParamsFromOptions(options);
135
+ // `arnsPriceParamsFromOptions` substitutes PRICING_PLACEHOLDER_PROCESS_ID for
136
+ // an omitted Buy-Name `processId`, which is fine for a price lookup but NOT
137
+ // here: a quote records a real purchase, and that placeholder is not a valid
138
+ // ANT. Send `processId` only when the caller actually supplied one — omitting
139
+ // it is what tells Turbo to custodially provision the ANT.
140
+ if (options.processId === undefined) {
141
+ delete params.processId;
142
+ }
143
+ const address = options.address;
144
+ if (address === undefined) {
145
+ throw new Error('A destination --address is required for a fiat ArNS quote.');
146
+ }
147
+ const client = turbo ?? TurboFactory.unauthenticated(configFromOptions(options));
148
+ try {
149
+ const { purchaseQuote, paymentSession, adjustments, fees } = await client.getArNSFiatPurchaseQuote({
150
+ ...params,
151
+ address,
152
+ currency: (options.currency ?? 'usd'),
153
+ method: options.method,
154
+ promoCodes: options.promoCode,
155
+ });
156
+ console.log(JSON.stringify({
157
+ name: purchaseQuote.name,
158
+ intent: purchaseQuote.intent,
159
+ nonce: purchaseQuote.nonce,
160
+ paymentAmount: purchaseQuote.paymentAmount,
161
+ currency: purchaseQuote.currencyType,
162
+ quoteExpirationDate: purchaseQuote.quoteExpirationDate,
163
+ paymentSessionId: paymentSession.id,
164
+ // Present for a payment intent / embedded checkout; a hosted
165
+ // checkout session returns `url` instead.
166
+ clientSecret: paymentSession.client_secret ?? undefined,
167
+ checkoutUrl: paymentSession.url ?? undefined,
168
+ adjustments,
169
+ fees,
170
+ }, null, 2));
171
+ }
172
+ catch (error) {
173
+ if (error instanceof FiatPaymentsDisabledError) {
174
+ console.error('Fiat (Stripe) payments are disabled on this payment service. Use the credit-paid commands (buy-arns-name, extend-arns-lease, ...) instead.');
175
+ throw error;
176
+ }
177
+ throw error;
178
+ }
179
+ }
133
180
  export async function buyArNSName(options, turbo) {
134
181
  const name = requiredNameFromOptions(options);
135
182
  const type = typeFromOptions(options.type);
@@ -301,6 +301,23 @@ export const arnsPriceOptions = [
301
301
  optionMap.arnsIncreaseQty,
302
302
  optionMap.arnsProcessId,
303
303
  ];
304
+ export const arnsFiatQuoteOptions = [
305
+ optionMap.arnsName,
306
+ optionMap.arnsType,
307
+ optionMap.arnsYears,
308
+ optionMap.arnsIncreaseQty,
309
+ optionMap.arnsProcessId,
310
+ optionMap.address,
311
+ optionMap.currency,
312
+ {
313
+ alias: '--method <method>',
314
+ description: 'Stripe integration mode: payment-intent (default) or checkout-session',
315
+ },
316
+ {
317
+ alias: '--promo-code <promoCode...>',
318
+ description: 'Promo code(s) to apply to the fiat price',
319
+ },
320
+ ];
304
321
  export const buyArNSNameOptions = [
305
322
  ...walletOptions,
306
323
  optionMap.arnsName,