@forgezero/providers 0.1.22 → 0.1.24

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.
@@ -336,369 +336,270 @@ function createService(definition, methods, options = {}) {
336
336
  }
337
337
  };
338
338
  }
339
- var VERSION = "0.1.22";
339
+ var VERSION = "0.1.24";
340
340
 
341
- // src/http.ts
342
- class BudgetExhausted extends ProviderError {
343
- host;
344
- retryAfterMs;
345
- constructor(host, retryAfterMs) {
346
- super("RATE_BUDGET_EXHAUSTED", `The ${host} budget is spent. Retry in ${retryAfterMs}ms.`);
347
- this.host = host;
348
- this.retryAfterMs = retryAfterMs;
341
+ // src/billing.ts
342
+ var PAYMENT_PROVIDER_KEYS = ["stripe:v1", "paypal:v1", "razorpay:v1"];
343
+ var paymentProviderDefinitions = [
344
+ {
345
+ id: "stripe",
346
+ label: "Stripe",
347
+ multiInstance: true,
348
+ config: {
349
+ type: "object",
350
+ properties: {
351
+ providerApiVersion: { type: "string", minLength: 4, maxLength: 64 },
352
+ mode: { type: "string", enum: ["test", "live"] }
353
+ },
354
+ required: ["providerApiVersion", "mode"],
355
+ additionalProperties: false
356
+ },
357
+ credentials: {
358
+ type: "object",
359
+ properties: {
360
+ secretKey: { type: "string", minLength: 16, maxLength: 512, writeOnly: true },
361
+ webhookSecret: { type: "string", minLength: 16, maxLength: 512, writeOnly: true }
362
+ },
363
+ required: ["secretKey", "webhookSecret"],
364
+ additionalProperties: false
365
+ },
366
+ methods: {}
367
+ },
368
+ {
369
+ id: "paypal",
370
+ label: "PayPal",
371
+ multiInstance: true,
372
+ config: {
373
+ type: "object",
374
+ properties: {
375
+ providerApiVersion: { type: "string", minLength: 2, maxLength: 64 },
376
+ environment: { type: "string", enum: ["sandbox", "live"] }
377
+ },
378
+ required: ["providerApiVersion", "environment"],
379
+ additionalProperties: false
380
+ },
381
+ credentials: {
382
+ type: "object",
383
+ properties: {
384
+ clientId: { type: "string", minLength: 10, maxLength: 512, writeOnly: true },
385
+ clientSecret: { type: "string", minLength: 10, maxLength: 512, writeOnly: true },
386
+ webhookId: { type: "string", minLength: 4, maxLength: 256, writeOnly: true }
387
+ },
388
+ required: ["clientId", "clientSecret", "webhookId"],
389
+ additionalProperties: false
390
+ },
391
+ methods: {}
392
+ },
393
+ {
394
+ id: "razorpay",
395
+ label: "Razorpay",
396
+ multiInstance: true,
397
+ config: {
398
+ type: "object",
399
+ properties: {
400
+ providerApiVersion: { type: "string", minLength: 2, maxLength: 64 },
401
+ mode: { type: "string", enum: ["test", "live"] }
402
+ },
403
+ required: ["providerApiVersion", "mode"],
404
+ additionalProperties: false
405
+ },
406
+ credentials: {
407
+ type: "object",
408
+ properties: {
409
+ keyId: { type: "string", minLength: 8, maxLength: 256, writeOnly: true },
410
+ keySecret: { type: "string", minLength: 8, maxLength: 512, writeOnly: true },
411
+ webhookSecret: { type: "string", minLength: 8, maxLength: 512, writeOnly: true }
412
+ },
413
+ required: ["keyId", "keySecret", "webhookSecret"],
414
+ additionalProperties: false
415
+ },
416
+ methods: {}
349
417
  }
350
- }
351
- var windows = new Map;
352
- function resetBudgets() {
353
- windows.clear();
354
- }
355
- function spend(budget, cost, nowMs) {
356
- const ceiling = Math.floor(budget.limit * (budget.headroom ?? 0.9));
357
- const current = windows.get(budget.host);
358
- if (!current || current.resetAtMs <= nowMs) {
359
- windows.set(budget.host, { spent: cost, resetAtMs: nowMs + budget.windowMs });
360
- return;
418
+ ];
419
+ var billingService = defineService({
420
+ key: "billing",
421
+ methods: {
422
+ createHostedSetup: serviceMethod(),
423
+ completeHostedSetup: serviceMethod(),
424
+ createCustomer: serviceMethod(),
425
+ charge: serviceMethod(),
426
+ detachPaymentMethod: serviceMethod(),
427
+ verifyWebhook: serviceMethod(),
428
+ reconcile: serviceMethod()
361
429
  }
362
- if (current.spent + cost > ceiling) {
363
- throw new BudgetExhausted(budget.host, current.resetAtMs - nowMs);
430
+ });
431
+ function required(value, field, maximum = 512) {
432
+ const result = value.trim();
433
+ if (!result || result.length > maximum)
434
+ throw new Error(`PAYMENT_${field.toUpperCase()}_INVALID`);
435
+ return result;
436
+ }
437
+ function assertHostedInput(input) {
438
+ required(input.tenantKey, "tenant");
439
+ required(input.idempotencyKey, "idempotency", 200);
440
+ for (const [field, value] of [["return_url", input.returnUrl], ["cancel_url", input.cancelUrl]]) {
441
+ const url = new URL(required(value, field, 2048));
442
+ if (url.protocol !== "https:")
443
+ throw new Error(`PAYMENT_${field.toUpperCase()}_INVALID`);
364
444
  }
365
- current.spent += cost;
366
445
  }
367
- function settle(host, reserved, actual) {
368
- const current = windows.get(host);
369
- if (!current)
370
- return;
371
- current.spent = Math.max(0, current.spent - reserved + actual);
446
+ function assertChargeInput(input) {
447
+ required(input.tenantKey, "tenant");
448
+ required(input.invoiceKey, "invoice");
449
+ required(input.providerCustomerReference, "customer_reference");
450
+ required(input.providerPaymentMethodReference, "method_reference");
451
+ required(input.idempotencyKey, "idempotency", 200);
452
+ if (!/^[1-9][0-9]*$/.test(input.amountMinor))
453
+ throw new Error("PAYMENT_AMOUNT_INVALID");
454
+ if (input.currency !== "USD")
455
+ throw new Error("PAYMENT_CURRENCY_INVALID");
372
456
  }
373
- var budgetState = (host) => windows.get(host);
374
- function createHttpClient(config) {
375
- const doFetch = config.fetch ?? globalThis.fetch;
376
- const timeoutMs = config.timeoutMs ?? 1e4;
377
- return {
378
- async call(request) {
379
- const reserved = request.weight ?? config.budget.defaultCost;
380
- spend(config.budget, reserved, Date.now());
381
- const url = new URL(config.baseUrl + request.path);
382
- for (const [key, value] of Object.entries(request.query ?? {})) {
383
- url.searchParams.set(key, String(value));
384
- }
385
- const controller = new AbortController;
386
- const timer = setTimeout(() => controller.abort(), timeoutMs);
387
- try {
388
- const response = await doFetch(url.toString(), {
389
- method: request.method ?? "GET",
390
- signal: controller.signal,
391
- headers: {
392
- ...request.body === undefined ? {} : { "content-type": "application/json" },
393
- ...request.headers
394
- },
395
- ...request.body === undefined ? {} : { body: JSON.stringify(request.body) }
396
- });
397
- const cost = config.costOf?.(response);
398
- if (cost !== undefined)
399
- settle(config.budget.host, reserved, cost);
400
- const text = await response.text();
401
- let body;
402
- try {
403
- body = text ? JSON.parse(text) : null;
404
- } catch {
405
- body = text;
406
- }
407
- if (!response.ok) {
408
- throw Object.assign(new Error(`${config.budget.host} ${response.status}`), {
409
- status: response.status,
410
- body,
411
- retryAfter: response.headers.get("retry-after")
412
- });
413
- }
414
- return { status: response.status, body, cost };
415
- } finally {
416
- clearTimeout(timer);
417
- }
418
- },
419
- budget: () => budgetState(config.budget.host)
420
- };
457
+ function assertSetupCompletion(input) {
458
+ required(input.tenantKey, "tenant");
459
+ required(input.setupReference, "setup_reference", 512);
460
+ required(input.idempotencyKey, "idempotency", 200);
421
461
  }
422
- var http = defineSingleMethodProvider({
423
- id: "http",
424
- method: "request",
425
- label: "HTTP",
426
- multiInstance: true,
427
- credentials: {
428
- type: "object",
429
- additionalProperties: false,
430
- properties: {
431
- apiKey: { type: "string", title: "API key", writeOnly: true },
432
- apiSecret: { type: "string", title: "API secret", writeOnly: true }
433
- }
434
- },
435
- config: {
436
- type: "object",
437
- additionalProperties: false,
438
- required: ["baseUrl"],
439
- properties: {
440
- baseUrl: { type: "string", title: "Base URL" },
441
- limit: { type: "integer", default: 6000, title: "Units per window" },
442
- windowMs: { type: "integer", default: 60000 },
443
- headroom: {
444
- type: "number",
445
- default: 0.9,
446
- description: "Stop at this fraction of the limit. The venue's window boundary is not ours, and the penalty for crossing is a ban rather than a rejection."
447
- }
448
- }
449
- },
450
- async invoke(context, request) {
451
- const config = context.config;
452
- const client = createHttpClient({
453
- baseUrl: config.baseUrl,
454
- fetch: config.fetch,
455
- costOf: config.costOf,
456
- budget: {
457
- host: new URL(config.baseUrl).host,
458
- limit: config.limit ?? 6000,
459
- windowMs: config.windowMs ?? 60000,
460
- defaultCost: 1,
461
- headroom: config.headroom
462
- }
463
- });
464
- return client.call(request);
465
- },
466
- classify(error) {
467
- const status = error.status;
468
- if (status === 418)
469
- return "backoff";
470
- if (status === 429)
471
- return "backoff";
472
- if (status === 400 || status === 422)
473
- return "terminal";
474
- if (status === 401 || status === 403)
475
- return "retryable";
476
- return "retryable";
477
- }
478
- });
479
- var binanceWeight = (response) => {
480
- const header = response.headers.get("x-mbx-used-weight-1m");
481
- return header === null ? undefined : Number(header);
482
- };
483
- var httpProviders = [http];
484
-
485
- // src/binance.ts
486
- import { parseAmount, formatAmount, zero, assetSpec } from "@forgezero/runtime/finance/money";
487
- import {
488
- parseSymbol,
489
- VenueError
490
- } from "@forgezero/runtime/finance/venues";
491
- var BUDGET_HOST = "binance";
492
- var DEFAULT_HOSTS = {
493
- spot: "https://api.binance.com",
494
- margin: "https://api.binance.com",
495
- futures: "https://fapi.binance.com"
496
- };
497
- var binanceSymbol = (symbol) => {
498
- const { base, quote } = parseSymbol(symbol);
499
- return `${base}${quote}`;
500
- };
501
- var PATHS = {
502
- spot: {
503
- order: "/api/v3/order",
504
- openOrders: "/api/v3/openOrders",
505
- account: "/api/v3/account",
506
- exchangeInfo: "/api/v3/exchangeInfo"
507
- },
508
- margin: {
509
- order: "/sapi/v1/margin/order",
510
- openOrders: "/sapi/v1/margin/openOrders",
511
- account: "/sapi/v1/margin/account",
512
- exchangeInfo: "/api/v3/exchangeInfo"
513
- },
514
- futures: {
515
- order: "/fapi/v1/order",
516
- openOrders: "/fapi/v1/openOrders",
517
- account: "/fapi/v2/account",
518
- exchangeInfo: "/fapi/v1/exchangeInfo"
462
+ function assertHostedResult(value) {
463
+ const hostedUrl = new URL(required(value.hostedUrl, "hosted_url", 2048));
464
+ if (hostedUrl.protocol !== "https:" || hostedUrl.username || hostedUrl.password)
465
+ throw new Error("PAYMENT_HOSTED_URL_INVALID");
466
+ required(value.setupReference, "setup_reference", 512);
467
+ if (!Number.isSafeInteger(value.expiresAtTs) || value.expiresAtTs <= Date.now())
468
+ throw new Error("PAYMENT_SETUP_EXPIRY_INVALID");
469
+ return { ...value, hostedUrl: hostedUrl.toString() };
470
+ }
471
+ function assertCompletionResult(value) {
472
+ required(value.providerPaymentMethodReference, "method_reference", 512);
473
+ if (value.providerCustomerReference !== undefined)
474
+ required(value.providerCustomerReference, "customer_reference", 512);
475
+ const display = value.display;
476
+ if (!display || !["card", "bank", "upi", "paypal", "other"].includes(display.type))
477
+ throw new Error("PAYMENT_DISPLAY_INVALID");
478
+ for (const [field, item, maximum] of [
479
+ ["brand", display.brand, 80],
480
+ ["last4", display.last4, 8],
481
+ ["label", display.label, 160]
482
+ ]) {
483
+ if (item !== undefined)
484
+ required(item, `display_${field}`, maximum);
519
485
  }
520
- };
521
- function toOrderStatus(status) {
522
- switch (status) {
523
- case "NEW":
524
- case "PENDING_NEW":
525
- return "accepted";
526
- case "PARTIALLY_FILLED":
527
- return "partial";
528
- case "FILLED":
529
- return "filled";
530
- case "CANCELED":
531
- case "PENDING_CANCEL":
532
- case "EXPIRED":
533
- case "EXPIRED_IN_MATCH":
534
- return "cancelled";
535
- default:
536
- return "rejected";
486
+ if (display.last4 !== undefined && !/^[A-Za-z0-9]{1,8}$/.test(display.last4))
487
+ throw new Error("PAYMENT_DISPLAY_LAST4_INVALID");
488
+ for (const value2 of [display.expiresMonth, display.expiresYear]) {
489
+ if (value2 !== undefined && !Number.isSafeInteger(value2))
490
+ throw new Error("PAYMENT_DISPLAY_EXPIRY_INVALID");
537
491
  }
492
+ if (display.expiresMonth !== undefined && (display.expiresMonth < 1 || display.expiresMonth > 12))
493
+ throw new Error("PAYMENT_DISPLAY_EXPIRY_INVALID");
494
+ return structuredClone(value);
538
495
  }
539
- var SIDE = { buy: "BUY", sell: "SELL" };
540
- var TYPE = { market: "MARKET", limit: "LIMIT", "stop-limit": "STOP_LOSS_LIMIT" };
541
- async function sign(secret, query) {
542
- const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
543
- const mac = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(query));
544
- return [...new Uint8Array(mac)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
496
+ function assertCustomerResult(value) {
497
+ return { providerCustomerReference: required(value.providerCustomerReference, "customer_reference", 512) };
545
498
  }
546
- function createBinanceAdapter(options) {
547
- const hosts = { ...DEFAULT_HOSTS, ...options.hosts };
548
- const recvWindow = options.recvWindowMs ?? 5000;
549
- const now = options.now ?? Date.now;
550
- const clients = new Map;
551
- const clientFor = (type) => {
552
- const base = hosts[type];
553
- const existing = clients.get(base);
554
- if (existing)
555
- return existing;
556
- const client = createHttpClient({
557
- baseUrl: base,
558
- fetch: options.fetch,
559
- costOf: binanceWeight,
560
- budget: { host: BUDGET_HOST, limit: 6000, windowMs: 60000, defaultCost: 1, headroom: 0.9 }
561
- });
562
- clients.set(base, client);
563
- return client;
564
- };
565
- async function signed(args) {
566
- const entries = Object.entries(args.params).filter(([, value]) => value !== undefined);
567
- const base = new URLSearchParams(entries.map(([name, value]) => [name, String(value)])).toString();
568
- const withTiming = `${base}${base ? "&" : ""}recvWindow=${recvWindow}&timestamp=${now()}`;
569
- const signature = await sign(options.credentials.apiSecret, withTiming);
570
- const response = await clientFor(args.type).call({
571
- path: `${args.path}?${withTiming}&signature=${signature}`,
572
- method: args.method,
573
- weight: args.weight,
574
- headers: { "X-MBX-APIKEY": options.credentials.apiKey }
575
- });
576
- return response.body;
577
- }
499
+ function assertChargeResult(value) {
500
+ const providerAttemptReference = required(value.providerAttemptReference, "attempt_reference", 512);
501
+ if (!["succeeded", "failed", "pending"].includes(value.status))
502
+ throw new Error("PAYMENT_STATUS_INVALID");
503
+ const failureCode = value.failureCode === undefined ? undefined : required(value.failureCode, "failure_code", 160);
504
+ return { providerAttemptReference, status: value.status, ...failureCode ? { failureCode } : {} };
505
+ }
506
+ function assertWebhookResult(value) {
507
+ const providerEventId = required(value.providerEventId, "event_id", 512);
508
+ const eventType = required(value.eventType, "event_type", 160);
509
+ const resourceReference = required(value.resourceReference, "resource_reference", 512);
510
+ if (!["succeeded", "failed", "pending"].includes(value.status))
511
+ throw new Error("PAYMENT_STATUS_INVALID");
512
+ if (!Number.isSafeInteger(value.occurredAtTs) || value.occurredAtTs < 0)
513
+ throw new Error("PAYMENT_EVENT_TIME_INVALID");
514
+ const failureCode = value.failureCode === undefined ? undefined : required(value.failureCode, "failure_code", 160);
578
515
  return {
579
- venue: "binance",
580
- symbolFor: (symbol) => binanceSymbol(symbol),
581
- async markets(type) {
582
- const info = await clientFor(type).call({ path: PATHS[type].exchangeInfo, weight: 20 });
583
- return (info.body.symbols ?? []).filter((entry) => entry.status === "TRADING").map((entry) => {
584
- const filter = (name) => entry.filters.find((candidate) => candidate.filterType === name);
585
- return {
586
- venue: "binance",
587
- type,
588
- symbol: `${entry.baseAsset}/${entry.quoteAsset}`,
589
- base: entry.baseAsset,
590
- quote: entry.quoteAsset,
591
- lotStep: filter("LOT_SIZE")?.stepSize ?? "0.00000001",
592
- tickStep: filter("PRICE_FILTER")?.tickSize ?? "0.00000001",
593
- minNotional: filter("NOTIONAL")?.minNotional ?? filter("MIN_NOTIONAL")?.minNotional ?? "0",
594
- ...type === "spot" ? {} : { maxLeverage: 125 }
595
- };
596
- });
516
+ providerEventId,
517
+ eventType,
518
+ resourceReference,
519
+ status: value.status,
520
+ occurredAtTs: value.occurredAtTs,
521
+ ...failureCode ? { failureCode } : {}
522
+ };
523
+ }
524
+ function assertReconciliationResult(value) {
525
+ const providerAttemptReference = required(value.providerAttemptReference, "attempt_reference", 512);
526
+ if (!["succeeded", "failed", "pending", "not_found"].includes(value.status))
527
+ throw new Error("PAYMENT_STATUS_INVALID");
528
+ const failureCode = value.failureCode === undefined ? undefined : required(value.failureCode, "failure_code", 160);
529
+ return { providerAttemptReference, status: value.status, ...failureCode ? { failureCode } : {} };
530
+ }
531
+ function paymentProviderAdapter(args) {
532
+ const [provider, contractVersion] = args.key.split(":");
533
+ const providerApiVersion = required(args.providerApiVersion, "api_version", 100);
534
+ return Object.freeze({
535
+ key: args.key,
536
+ provider,
537
+ contractVersion,
538
+ providerApiVersion,
539
+ async createHostedSetup(input) {
540
+ assertHostedInput(input);
541
+ return assertHostedResult(await args.driver.createHostedSetup(input));
597
542
  },
598
- async placeOrder(request, market) {
599
- if (request.type === "futures" && request.leverage) {
600
- await signed({
601
- type: "futures",
602
- path: "/fapi/v1/leverage",
603
- method: "POST",
604
- params: { symbol: binanceSymbol(request.symbol), leverage: request.leverage }
605
- });
606
- }
607
- const raw = await signed({
608
- type: request.type,
609
- path: PATHS[request.type].order,
610
- method: "POST",
611
- weight: 1,
612
- params: {
613
- symbol: binanceSymbol(request.symbol),
614
- side: SIDE[request.side],
615
- type: TYPE[request.orderType],
616
- quantity: formatAmount(request.quantity, { trim: true }),
617
- price: request.price ? formatAmount(request.price, { trim: true }) : undefined,
618
- stopPrice: request.stopPrice ? formatAmount(request.stopPrice, { trim: true }) : undefined,
619
- timeInForce: request.orderType === "market" ? undefined : (request.timeInForce ?? "gtc").toUpperCase(),
620
- newClientOrderId: request.clientOrderId,
621
- ...request.type === "margin" ? { sideEffectType: "NO_SIDE_EFFECT" } : {},
622
- ...request.dryRun ? { test: "true" } : {}
623
- }
624
- });
625
- return readOrder(raw, market);
543
+ async completeHostedSetup(input) {
544
+ assertSetupCompletion(input);
545
+ return assertCompletionResult(await args.driver.completeHostedSetup(input));
626
546
  },
627
- async cancelOrder(args) {
628
- await signed({
629
- type: args.type,
630
- path: PATHS[args.type].order,
631
- method: "DELETE",
632
- params: { symbol: binanceSymbol(args.symbol), orderId: args.venueOrderId }
633
- });
547
+ async createCustomer(input) {
548
+ required(input.tenantKey, "tenant");
549
+ required(input.email, "email");
550
+ required(input.idempotencyKey, "idempotency", 200);
551
+ return assertCustomerResult(await args.driver.createCustomer(input));
634
552
  },
635
- async openOrders(args) {
636
- const raw = await signed({
637
- type: args.type,
638
- path: PATHS[args.type].openOrders,
639
- method: "GET",
640
- weight: args.symbol ? 3 : 40,
641
- params: { symbol: args.symbol ? binanceSymbol(args.symbol) : undefined }
642
- });
643
- return (raw ?? []).map((entry) => readOrder(entry, undefined));
553
+ async charge(input) {
554
+ assertChargeInput(input);
555
+ return assertChargeResult(await args.driver.charge(input));
644
556
  },
645
- async balances(type) {
646
- const raw = await signed({ type, path: PATHS[type].account, method: "GET", weight: 10, params: {} });
647
- const rows = raw.balances ?? raw.userAssets ?? (raw.assets ?? []).map((entry) => ({ asset: entry.asset, free: entry.availableBalance }));
648
- return rows.filter((entry) => Number(entry.free) > 0).map((entry) => {
649
- try {
650
- return parseAmount(entry.free, entry.asset);
651
- } catch {
652
- return null;
653
- }
654
- }).filter((amount) => amount !== null);
557
+ async detachPaymentMethod(input) {
558
+ required(input.tenantKey, "tenant");
559
+ required(input.providerPaymentMethodReference, "method_reference");
560
+ required(input.idempotencyKey, "idempotency", 200);
561
+ return args.driver.detachPaymentMethod(input);
562
+ },
563
+ async verifyWebhook(input) {
564
+ if (!(input.rawBody instanceof Uint8Array) || input.rawBody.byteLength > 1048576) {
565
+ throw new Error("PAYMENT_WEBHOOK_BODY_INVALID");
566
+ }
567
+ return assertWebhookResult(await args.driver.verifyWebhook(input));
568
+ },
569
+ async reconcile(input) {
570
+ required(input.tenantKey, "tenant");
571
+ required(input.providerAttemptReference, "attempt_reference");
572
+ return assertReconciliationResult(await args.driver.reconcile(input));
655
573
  }
656
- };
657
- }
658
- function readOrder(raw, market) {
659
- const asset = market?.base ?? "BTC";
660
- const executed = String(raw.executedQty ?? raw.origQty ?? "0");
661
- let filledQuantity;
662
- try {
663
- filledQuantity = parseAmount(executed, asset);
664
- } catch {
665
- filledQuantity = zero(asset);
666
- }
667
- const quoteFilled = Number(raw.cummulativeQuoteQty ?? 0);
668
- const filled = Number(executed);
669
- return {
670
- venueOrderId: String(raw.orderId ?? ""),
671
- clientOrderId: raw.clientOrderId ? String(raw.clientOrderId) : undefined,
672
- status: toOrderStatus(String(raw.status ?? "NEW")),
673
- filledQuantity,
674
- ...market && filled > 0 && quoteFilled > 0 ? {
675
- averagePrice: (() => {
676
- try {
677
- return parseAmount((quoteFilled / filled).toFixed(assetSpec(market.quote).decimals), market.quote);
678
- } catch {
679
- return;
680
- }
681
- })()
682
- } : {},
683
- raw
684
- };
574
+ });
685
575
  }
686
- function readBinanceError(error) {
687
- const body = error.body;
688
- if (!body?.code)
689
- return;
690
- const known = {
691
- [-1013]: ["MIN_NOTIONAL", "The order is below the venue minimum, or off its lot or tick step."],
692
- [-2010]: ["MIN_NOTIONAL", "Rejected: insufficient balance, or below the minimum."],
693
- [-1111]: ["LOT_STEP", "More decimal places than this market accepts."],
694
- [-1121]: ["UNKNOWN_MARKET", "That symbol is not traded on this venue."]
695
- };
696
- const match = known[body.code];
697
- return match ? new VenueError(match[0], `${match[1]} (${body.msg})`) : undefined;
576
+ function definePaymentProvider(args) {
577
+ const classify = args.classify ?? (() => "retryable");
578
+ const branch = (invoke) => defineProviderMethodBranches({
579
+ currentVersion: args.version,
580
+ versions: { [args.version]: { version: args.version, lifecycle: "current", invoke, classify } }
581
+ });
582
+ return defineProvider({
583
+ id: args.id,
584
+ label: args.label,
585
+ multiInstance: true,
586
+ config: args.config,
587
+ credentials: args.credentials,
588
+ methods: {
589
+ createHostedSetup: branch(args.driver.createHostedSetup),
590
+ completeHostedSetup: branch(args.driver.completeHostedSetup),
591
+ createCustomer: branch(args.driver.createCustomer),
592
+ charge: branch(args.driver.charge),
593
+ detachPaymentMethod: branch(args.driver.detachPaymentMethod),
594
+ verifyWebhook: branch(args.driver.verifyWebhook),
595
+ reconcile: branch(args.driver.reconcile)
596
+ }
597
+ });
698
598
  }
699
599
  export {
700
- toOrderStatus,
701
- readBinanceError,
702
- createBinanceAdapter,
703
- binanceSymbol
600
+ paymentProviderDefinitions,
601
+ paymentProviderAdapter,
602
+ definePaymentProvider,
603
+ billingService,
604
+ PAYMENT_PROVIDER_KEYS
704
605
  };
package/dist/database.js CHANGED
@@ -336,7 +336,7 @@ function createService(definition, methods, options = {}) {
336
336
  }
337
337
  };
338
338
  }
339
- var VERSION = "0.1.22";
339
+ var VERSION = "0.1.24";
340
340
 
341
341
  // src/database.ts
342
342
  var pools = new Map;
package/dist/email.js CHANGED
@@ -336,7 +336,7 @@ function createService(definition, methods, options = {}) {
336
336
  }
337
337
  };
338
338
  }
339
- var VERSION = "0.1.22";
339
+ var VERSION = "0.1.24";
340
340
 
341
341
  // src/email.ts
342
342
  var recipients = (to) => Array.isArray(to) ? [...to] : [to];