@coin-voyage/shared 2.2.3 → 2.2.5-beta.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.
@@ -1,5 +1,5 @@
1
1
  import { type PayOrder, PayOrderMode } from "../types";
2
- import type { ClaimFeesRequest, ClaimFeesResponse, CreateWebhookRequest, GetFeeBalancesResponse, PaymentDetails, PaymentDetailsParams, PayOrderParams, PayOrderQuoteParams, QuoteWithBalance, SwapDataRequest, SwapDataResponse, SwapQuoteRequest, UpdateWebhookRequest, WebhookResponse } from "../types/api";
2
+ import type { ClaimFeesRequest, ClaimFeesResponse, CreateWebhookRequest, GetFeeBalancesResponse, OrderStatusSocket, PaymentDetails, PaymentDetailsParams, PayOrderParams, PayOrderQuoteParams, QuoteWithBalance, SwapDataRequest, SwapDataResponse, SwapQuoteRequest, UpdateWebhookRequest, WebhookResponse } from "../types/api";
3
3
  import { type APIEnvironment } from "./config";
4
4
  import { type APIResponse } from "./fetcher";
5
5
  export declare class ApiClient {
@@ -272,4 +272,19 @@ export declare class ApiClient {
272
272
  * @returns {Promise<APIResponse<SwapDataResponse>>} - Swap data wrapped in an API response.
273
273
  */
274
274
  swapData(params: SwapDataRequest): Promise<APIResponse<SwapDataResponse>>;
275
+ /**
276
+ * Opens a WebSocket connection to receive real-time order status events.
277
+ *
278
+ * Subscribe to an order's events after the connection is open:
279
+ * ```ts
280
+ * const socket = apiClient.subscribeOrderStatus()
281
+ * socket.onOpen(() => socket.subscribe(payorderId))
282
+ * socket.onMessage((msg) => {
283
+ * if (msg.type === 'event') console.log(msg.data)
284
+ * })
285
+ * ```
286
+ *
287
+ * @returns {OrderStatusSocket} - Handle for subscribing, receiving messages, and closing the connection.
288
+ */
289
+ subscribeOrderStatus(): OrderStatusSocket;
275
290
  }
@@ -1,6 +1,7 @@
1
1
  import { createHmac } from "crypto";
2
2
  import { now, zPayOrder, zPayOrderMetadata } from "../common";
3
3
  import { PayOrderMode } from "../types";
4
+ import { WS_URL } from "./config";
4
5
  import { fetchApi } from "./fetcher";
5
6
  export class ApiClient {
6
7
  constructor({ apiKey, sessionId, environment = "production", version, }) {
@@ -461,4 +462,76 @@ export class ApiClient {
461
462
  },
462
463
  });
463
464
  }
465
+ // ===================
466
+ // WebSocket
467
+ // ===================
468
+ /**
469
+ * Opens a WebSocket connection to receive real-time order status events.
470
+ *
471
+ * Subscribe to an order's events after the connection is open:
472
+ * ```ts
473
+ * const socket = apiClient.subscribeOrderStatus()
474
+ * socket.onOpen(() => socket.subscribe(payorderId))
475
+ * socket.onMessage((msg) => {
476
+ * if (msg.type === 'event') console.log(msg.data)
477
+ * })
478
+ * ```
479
+ *
480
+ * @returns {OrderStatusSocket} - Handle for subscribing, receiving messages, and closing the connection.
481
+ */
482
+ subscribeOrderStatus() {
483
+ const ws = new WebSocket(WS_URL[this.environment ?? "production"]);
484
+ const messageListeners = [];
485
+ const pendingMessages = [];
486
+ ws.addEventListener("open", () => {
487
+ ws.send(JSON.stringify({ type: "connect", data: { api_key: this.apiKey } }));
488
+ });
489
+ ws.addEventListener("message", (event) => {
490
+ try {
491
+ const msg = JSON.parse(event.data);
492
+ if (msg.type === "connected") {
493
+ for (const pending of pendingMessages)
494
+ ws.send(pending);
495
+ pendingMessages.length = 0;
496
+ }
497
+ for (const listener of messageListeners)
498
+ listener(msg);
499
+ }
500
+ catch {
501
+ // ignore malformed messages
502
+ }
503
+ });
504
+ const send = (msg) => {
505
+ const data = JSON.stringify(msg);
506
+ if (ws.readyState === WebSocket.OPEN) {
507
+ ws.send(data);
508
+ }
509
+ else {
510
+ pendingMessages.push(data);
511
+ }
512
+ };
513
+ return {
514
+ subscribe(payorderId) {
515
+ send({ type: "subscribe", data: { payorder_id: payorderId } });
516
+ },
517
+ unsubscribe(payorderId) {
518
+ send({ type: "unsubscribe", data: { payorder_id: payorderId } });
519
+ },
520
+ onMessage(callback) {
521
+ messageListeners.push(callback);
522
+ },
523
+ onOpen(callback) {
524
+ ws.addEventListener("open", callback);
525
+ },
526
+ onClose(callback) {
527
+ ws.addEventListener("close", callback);
528
+ },
529
+ onError(callback) {
530
+ ws.addEventListener("error", callback);
531
+ },
532
+ close() {
533
+ ws.close();
534
+ },
535
+ };
536
+ }
464
537
  }
@@ -1,2 +1,3 @@
1
1
  export declare const API_URL: Record<string, string>;
2
+ export declare const WS_URL: Record<string, string>;
2
3
  export type APIEnvironment = keyof typeof API_URL;
@@ -3,3 +3,8 @@ export const API_URL = {
3
3
  development: "https://acc-api.coinvoyage.io/v2",
4
4
  local: "http://localhost:8000/v2",
5
5
  };
6
+ export const WS_URL = {
7
+ production: "wss://api.coinvoyage.io/v2/ws",
8
+ development: "wss://acc-api.coinvoyage.io/v2/ws",
9
+ local: "ws://localhost:8000/v2/ws",
10
+ };
@@ -1,6 +1,7 @@
1
1
  import { FiatCurrency } from "../common/currencies";
2
2
  import { ChainId, ChainType, PayOrderStatus } from "./enums";
3
- import { Currency, CurrencyAmount, CurrencyBase, CurrencyWithAmount, PaymentData, PayOrderMetadataInput, QuoteWithCurrency } from "./model";
3
+ import { PayOrderEvent } from "./events";
4
+ import { Currency, CurrencyAmount, CurrencyBase, CurrencyWithAmount, PaymentData, PayOrderMetadata, QuoteWithCurrency } from "./model";
4
5
  export type PayOrderParams = {
5
6
  /**
6
7
  * Intent of the order.
@@ -9,7 +10,7 @@ export type PayOrderParams = {
9
10
  /**
10
11
  * Metadata to attach to the payOrder.
11
12
  */
12
- metadata?: PayOrderMetadataInput;
13
+ metadata?: PayOrderMetadata;
13
14
  };
14
15
  export type PayOrderQuoteParams = {
15
16
  wallet_address: string;
@@ -127,3 +128,43 @@ export type SwapDataResponse = {
127
128
  refund_address: string;
128
129
  expires_at: string;
129
130
  };
131
+ declare enum WsMessageType {
132
+ Connected = "connected",
133
+ Subscription = "subscription",
134
+ Event = "event",
135
+ Error = "error"
136
+ }
137
+ export type WsConnectedMessage = {
138
+ type: WsMessageType.Connected;
139
+ data: {
140
+ status: "authenticated";
141
+ };
142
+ };
143
+ export type WsSubscriptionMessage = {
144
+ type: WsMessageType.Subscription;
145
+ data: {
146
+ payorder_id: string;
147
+ status: "subscribed" | "unsubscribed";
148
+ };
149
+ };
150
+ export type WsEventMessage = {
151
+ type: WsMessageType.Event;
152
+ data: PayOrderEvent;
153
+ };
154
+ export type WsErrorMessage = {
155
+ type: WsMessageType.Error;
156
+ data: {
157
+ message: string;
158
+ };
159
+ };
160
+ export type WsServerMessage = WsConnectedMessage | WsSubscriptionMessage | WsEventMessage | WsErrorMessage;
161
+ export type OrderStatusSocket = {
162
+ subscribe: (payorderId: string) => void;
163
+ unsubscribe: (payorderId: string) => void;
164
+ onMessage: (callback: (msg: WsServerMessage) => void) => void;
165
+ onOpen: (callback: () => void) => void;
166
+ onClose: (callback: (event: CloseEvent) => void) => void;
167
+ onError: (callback: (event: Event) => void) => void;
168
+ close: () => void;
169
+ };
170
+ export {};
package/dist/types/api.js CHANGED
@@ -1 +1,9 @@
1
+ // ======== WebSocket ========
2
+ var WsMessageType;
3
+ (function (WsMessageType) {
4
+ WsMessageType["Connected"] = "connected";
5
+ WsMessageType["Subscription"] = "subscription";
6
+ WsMessageType["Event"] = "event";
7
+ WsMessageType["Error"] = "error";
8
+ })(WsMessageType || (WsMessageType = {}));
1
9
  export {};
@@ -1,4 +1,4 @@
1
- import { PayOrderMetadata } from "./model";
1
+ import { ParsedPayOrderMetadata } from "./model";
2
2
  import { Hex } from "./crypto";
3
3
  import { PaymentData } from "./model";
4
4
  export type PayOrderCreationErrorEvent = {
@@ -7,7 +7,7 @@ export type PayOrderCreationErrorEvent = {
7
7
  };
8
8
  type EventBase = {
9
9
  payorder_id: string;
10
- metadata?: PayOrderMetadata;
10
+ metadata?: ParsedPayOrderMetadata;
11
11
  };
12
12
  export type PayOrderCreatedEvent = EventBase & {
13
13
  type: "payorder_created";
@@ -3,3 +3,4 @@ export * from "./crypto";
3
3
  export * from "./enums";
4
4
  export * from "./events";
5
5
  export * from "./model";
6
+ export * from "./webhooks";
@@ -3,3 +3,4 @@ export * from "./crypto";
3
3
  export * from "./enums";
4
4
  export * from "./events";
5
5
  export * from "./model";
6
+ export * from "./webhooks";
@@ -1,30 +1,23 @@
1
1
  import { z } from "zod";
2
- import { zPayOrderMetadata } from "../common";
2
+ import { zPayOrderMetadata } from "../common/validation";
3
3
  import { FiatCurrency } from "../common/currencies";
4
- import { ChainId, PayOrderMode, PayOrderStatus, ProviderStatus, WebhookEventType } from "./enums";
5
- import { PayOrderEvent } from "./events";
4
+ import { ChainId, PayOrderMode, PayOrderStatus, ProviderStatus } from "./enums";
6
5
  export type PayOrder = {
7
6
  id: string;
8
7
  mode: PayOrderMode;
9
8
  status: PayOrderStatus;
10
- metadata?: PayOrderMetadata;
9
+ metadata?: ParsedPayOrderMetadata;
11
10
  deposit_tx_hash?: string;
12
11
  receiving_tx_hash?: string;
13
12
  refund_tx_hash?: string;
14
13
  fulfillment: FulfillmentData;
15
14
  payment?: PaymentData;
16
15
  };
17
- export type PayOrderMetadata = z.infer<typeof zPayOrderMetadata> & {
18
- [key: string]: string | number | boolean | any[] | {
19
- [x: string]: any;
20
- };
21
- };
22
- export type PayOrderMetadataInput = {
23
- items?: z.input<typeof zPayOrderMetadata.shape.items>;
24
- refund?: z.input<typeof zPayOrderMetadata.shape.refund>;
25
- } & Record<string, string | number | boolean | any[] | {
16
+ type MetadataExtraFields = Record<string, string | number | boolean | any[] | {
26
17
  [x: string]: any;
27
- }>;
18
+ } | undefined>;
19
+ export type ParsedPayOrderMetadata = z.output<typeof zPayOrderMetadata> & MetadataExtraFields;
20
+ export type PayOrderMetadata = z.input<typeof zPayOrderMetadata> & MetadataExtraFields;
28
21
  export interface Currency extends CurrencyBase {
29
22
  id?: string;
30
23
  name: string;
@@ -114,32 +107,8 @@ export type ExecutionStep = {
114
107
  fee_error?: unknown;
115
108
  price_impact?: string;
116
109
  };
117
- export interface WebhookEndpoint {
118
- id: string;
119
- organization_id: string;
120
- active: boolean;
121
- url: string;
122
- webhook_secret: string;
123
- subscription_events: WebhookEventType[];
124
- created_at: Date;
125
- }
126
- export interface WebhookEvent {
127
- id: string;
128
- endpoint: WebhookEndpoint;
129
- payload: PayOrderEvent;
130
- webhook_id: string;
131
- status: WebhookEventType;
132
- deliveries: WebhookDelivery[];
133
- created_at: Date;
134
- }
135
- export interface WebhookDelivery {
136
- id: string;
137
- event_id: string;
138
- http_status: number;
139
- body: string | null;
140
- created_at: Date;
141
- }
142
110
  export interface FeePlan extends CurrencyWithAmount {
143
111
  fee_bps?: number;
144
112
  }
145
113
  export type BigIntStr = `${bigint}`;
114
+ export {};
@@ -0,0 +1,27 @@
1
+ import { WebhookEventType } from "./enums";
2
+ import { PayOrderEvent } from "./events";
3
+ export interface WebhookEndpoint {
4
+ id: string;
5
+ organization_id: string;
6
+ active: boolean;
7
+ url: string;
8
+ webhook_secret: string;
9
+ subscription_events: WebhookEventType[];
10
+ created_at: Date;
11
+ }
12
+ export interface WebhookEvent {
13
+ id: string;
14
+ endpoint: WebhookEndpoint;
15
+ payload: PayOrderEvent;
16
+ webhook_id: string;
17
+ status: WebhookEventType;
18
+ deliveries: WebhookDelivery[];
19
+ created_at: Date;
20
+ }
21
+ export interface WebhookDelivery {
22
+ id: string;
23
+ event_id: string;
24
+ http_status: number;
25
+ body: string | null;
26
+ created_at: Date;
27
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@coin-voyage/shared",
3
3
  "description": "Shared utilities for Coin Voyage",
4
- "version": "2.2.3",
4
+ "version": "2.2.5-beta.0",
5
5
  "private": false,
6
6
  "sideEffects": false,
7
7
  "exports": {