@chaindaddy/js 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/api.d.ts CHANGED
@@ -38,8 +38,9 @@ export interface paths {
38
38
  * List the apps on a verified coin's page
39
39
  * @description The apps a verified coin's page shows, in page order: each app's ID, name, description, version,
40
40
  * category, icon and whether it is a built-in. It names the apps; it does not include what the owner
41
- * set up inside them. Apps turned off are left out, and so is every app rated NSFW and every app the
42
- * page itself no longer shows (not reviewed yet, suspended or removed). Answers 404 for a ticker
41
+ * set up inside them. Apps turned off are left out, and so is every app rated NSFW, every app the
42
+ * page itself no longer shows (not reviewed yet, suspended or removed) and the built-in parts of
43
+ * the page that are not apps (its header, its market panel). Answers 404 for a ticker
43
44
  * with no verified page on that chain, and for a page Chain Daddy removed. Keyless, the same for
44
45
  * every caller, and cached for about a minute.
45
46
  */
@@ -103,6 +104,30 @@ export interface paths {
103
104
  patch?: never;
104
105
  trace?: never;
105
106
  };
107
+ "/api/v2/iap/config": {
108
+ parameters: {
109
+ query?: never;
110
+ header?: never;
111
+ path?: never;
112
+ cookie?: never;
113
+ };
114
+ /**
115
+ * Store configuration
116
+ * @description How creator stores work right now: whether they are switched on, the chains a store can run
117
+ * on, what an item on each chain can be priced in besides the store's own token (the chain's
118
+ * coin, a dollar stablecoin), and where the receipt signing keys are published. It also says
119
+ * whether test stores are available here (`testMode`) and whether checkout sessions are
120
+ * (`checkoutSessions`, with the limits on how long one may stay open).
121
+ */
122
+ get: operations["getIapConfig"];
123
+ put?: never;
124
+ post?: never;
125
+ delete?: never;
126
+ options?: never;
127
+ head?: never;
128
+ patch?: never;
129
+ trace?: never;
130
+ };
106
131
  "/api/v2/iap/jwks": {
107
132
  parameters: {
108
133
  query?: never;
@@ -139,6 +164,10 @@ export interface paths {
139
164
  * amount paid, when) with its current status. A refunded order's receipt reads `revoked`: offline
140
165
  * verification proves a receipt was issued, this proves it still stands. The receipt is the compact JWS
141
166
  * exactly as issued (three base64url parts joined by dots).
167
+ *
168
+ * The answer is `200` whatever the receipt is. Act on `valid`, which is true only for status `valid`.
169
+ * A string that is not a receipt Chain Daddy signed (a bad signature, a changed payload, an unknown
170
+ * key, not a JWS at all) answers `{"valid": false, "status": "invalid", "error": "…"}` with no `payload`.
142
171
  */
143
172
  post: operations["verifyIapReceipt"];
144
173
  delete?: never;
@@ -167,6 +196,43 @@ export interface paths {
167
196
  patch?: never;
168
197
  trace?: never;
169
198
  };
199
+ "/api/v2/iap/stores/{storeId}/test-store": {
200
+ parameters: {
201
+ query?: never;
202
+ header?: never;
203
+ path: {
204
+ /** @description The store's ID (a UUID) */
205
+ storeId: components["parameters"]["IapStoreId"];
206
+ };
207
+ cookie?: never;
208
+ };
209
+ /**
210
+ * Get a store's test store
211
+ * @description The test store that stands beside a live store: the same coin page and payout, its own
212
+ * catalog, keys, orders and webhook, and `mode: test`. Its orders are paid with
213
+ * `POST /api/v2/iap/orders/{orderId}/test-pay`; nothing is sent on chain and no fee is taken. Pass the
214
+ * live store's id (a test store's own id returns that test store). Your account credentials, as a wallet
215
+ * that manages the coin's page; a store key is refused (403 `SESSION_REQUIRED`).
216
+ * Guide: https://docs.chaindaddy.io/developer/test-your-integration#test-store
217
+ */
218
+ get: operations["getIapTestStore"];
219
+ put?: never;
220
+ /**
221
+ * Create a store's test store
222
+ * @description Creates the live store's test store with a copy of its catalog as it is now, drafts included and
223
+ * nothing sold (201), or returns the one it already has (200). Safe to call again. From then on the
224
+ * two catalogs are separate. Make its keys with `POST /api/v2/iap/stores/{storeId}/keys` on the test
225
+ * store's id: they start with `cd_iap_test_` and work only on it. It takes no body. Your account
226
+ * credentials, as a wallet that manages the coin's page and has the plan a store needs.
227
+ * Guide: https://docs.chaindaddy.io/developer/test-your-integration#test-store
228
+ */
229
+ post: operations["openIapTestStore"];
230
+ delete?: never;
231
+ options?: never;
232
+ head?: never;
233
+ patch?: never;
234
+ trace?: never;
235
+ };
170
236
  "/api/v2/iap/stores/{storeId}/skus": {
171
237
  parameters: {
172
238
  query?: never;
@@ -714,9 +780,8 @@ export interface paths {
714
780
  * (200, secret unchanged; also turns back on a webhook turned off after repeated failures). Events default
715
781
  * to every `iap.*` type there is at the time of the save (a type added later needs another save); names
716
782
  * may omit the `iap.` prefix. The URL must be https and must not resolve to a
717
- * private address. Needs the store's own plan (a Developer plan or developer program membership), not
718
- * the Developer plan the general `/api/v2/webhooks` routes need. Account credentials, or a store key
719
- * with `webhooks:write`.
783
+ * private address. Needs only the plan the store itself needs (any paid plan), not the Developer plan the
784
+ * general `/api/v2/webhooks` routes need. Account credentials, or a store key with `webhooks:write`.
720
785
  */
721
786
  put: operations["setIapStoreWebhook"];
722
787
  post?: never;
@@ -873,6 +938,36 @@ export interface paths {
873
938
  patch?: never;
874
939
  trace?: never;
875
940
  };
941
+ "/api/v2/iap/orders/{orderId}/test-pay": {
942
+ parameters: {
943
+ query?: never;
944
+ header?: never;
945
+ path: {
946
+ /** @description `0x` followed by 64 hex characters */
947
+ orderId: components["parameters"]["IapOrderId"];
948
+ };
949
+ cookie?: never;
950
+ };
951
+ get?: never;
952
+ put?: never;
953
+ /**
954
+ * Pay a test order
955
+ * @description Pays a pending order of a test store with a simulated payment and confirms it at once: the items
956
+ * are granted, the receipt is signed and `order.confirmed` is written, exactly as for a real payment.
957
+ * No chain is read or written and no money moves. Where a transaction hash would be, the order carries
958
+ * `test:` and an id. Calling it again on a confirmed order returns that order and changes nothing.
959
+ *
960
+ * Call it with a key of the order's test store (`cd_iap_test_…`, scope `orders:write`), or signed in as
961
+ * a wallet that manages the coin's page. It takes no body. A live store's order is only ever paid on
962
+ * chain (409 `LIVE_ORDER`). Guide: https://docs.chaindaddy.io/developer/test-your-integration#test-store
963
+ */
964
+ post: operations["testPayIapOrder"];
965
+ delete?: never;
966
+ options?: never;
967
+ head?: never;
968
+ patch?: never;
969
+ trace?: never;
970
+ };
876
971
  "/api/v2/iap/checkout/sessions": {
877
972
  parameters: {
878
973
  query?: never;
@@ -1446,6 +1541,112 @@ export interface components {
1446
1541
  award?: components["schemas"]["IapAirdropAward"];
1447
1542
  campaign?: components["schemas"]["IapAwardAirdrop"];
1448
1543
  };
1544
+ IapChain: {
1545
+ /**
1546
+ * @description CAIP-2 chain id
1547
+ * @example eip155:8453
1548
+ */
1549
+ chainId?: string;
1550
+ /** @example base */
1551
+ chainKey?: string;
1552
+ /**
1553
+ * @description The checkout contract an order with a fee is paid through. Absent on Solana. A hint only: the
1554
+ * contract to pay is always the one in the order's own `payment` block.
1555
+ */
1556
+ checkout?: string;
1557
+ /**
1558
+ * @description The checkout contract an order with no fee is paid through (a store whose `feeBps` is 0). Absent on
1559
+ * Solana and where the chain has none. A hint only, like `checkout`.
1560
+ */
1561
+ checkoutNoFee?: string;
1562
+ /** @description Confirmations an order waits for before it is `confirmed` */
1563
+ confirmations?: number;
1564
+ /**
1565
+ * @description What a SKU on this chain may be paid in besides the store's own token (always allowed, and the
1566
+ * default): the chain's coin and, where the chain has one, its dollar stable.
1567
+ */
1568
+ currencies?: components["schemas"]["IapCurrency"][];
1569
+ };
1570
+ IapCurrency: {
1571
+ /** @enum {string} */
1572
+ currency?: "native" | "usdc";
1573
+ /**
1574
+ * @description The zero address for an EVM chain's coin, `11111111111111111111111111111111` for SOL, otherwise
1575
+ * the stable's contract or mint
1576
+ */
1577
+ address?: string;
1578
+ /** @example USDC */
1579
+ symbol?: string;
1580
+ /** @example 6 */
1581
+ decimals?: number;
1582
+ };
1583
+ IapConfig: {
1584
+ enabled?: boolean;
1585
+ /**
1586
+ * @description Whether test stores are available here: a store's test store, its `cd_iap_test_` keys, and paying a
1587
+ * test order with no money
1588
+ */
1589
+ testMode?: boolean;
1590
+ chains?: components["schemas"]["IapChain"][];
1591
+ /** @description The read-only account every Solana store payment references */
1592
+ solanaStaticKey?: string;
1593
+ /** @example https://api.chaindaddy.io/api/v2/iap/jwks */
1594
+ jwksUrl?: string;
1595
+ /**
1596
+ * @description The limits on what an app can make a buyer pay (paid apps). Chain Daddy may change them at any time.
1597
+ * Amounts are in millionths of a dollar.
1598
+ */
1599
+ paidApps?: {
1600
+ /**
1601
+ * Format: int64
1602
+ * @description The most one purchase through an app may cost; an app item priced above it cannot be saved
1603
+ * @example 250000000
1604
+ */
1605
+ maxOrderUsdMicros?: number;
1606
+ /**
1607
+ * Format: int64
1608
+ * @description The most one buyer wallet may pay one app in any 24 hours
1609
+ * @example 1000000000
1610
+ */
1611
+ walletAppDayUsdMicros?: number;
1612
+ /**
1613
+ * Format: int64
1614
+ * @description Above this, the purchase sheet asks the buyer to press and hold
1615
+ * @example 50000000
1616
+ */
1617
+ confirmAboveUsdMicros?: number;
1618
+ /**
1619
+ * @description The most units in one order through an app
1620
+ * @example 10
1621
+ */
1622
+ maxQuantity?: number;
1623
+ /**
1624
+ * @description The most orders one buyer wallet may open through one app in a minute
1625
+ * @example 5
1626
+ */
1627
+ ordersPerMinute?: number;
1628
+ };
1629
+ /** @description Present only where checkout sessions are switched on */
1630
+ checkoutSessions?: {
1631
+ /** @description When true, only a test store's key can open a checkout session here */
1632
+ testOnly?: boolean;
1633
+ /**
1634
+ * @description The least `expiresInSeconds` a session may ask for
1635
+ * @example 900
1636
+ */
1637
+ minExpiresInSeconds?: number;
1638
+ /**
1639
+ * @description The most `expiresInSeconds` a session may ask for
1640
+ * @example 86400
1641
+ */
1642
+ maxExpiresInSeconds?: number;
1643
+ /**
1644
+ * @description How long a session stays open when it does not say
1645
+ * @example 3600
1646
+ */
1647
+ defaultExpiresInSeconds?: number;
1648
+ };
1649
+ };
1449
1650
  IapStore: {
1450
1651
  /** Format: uuid */
1451
1652
  id?: string;
@@ -1483,6 +1684,12 @@ export interface components {
1483
1684
  * for its own items; items from apps installed on the page keep selling.
1484
1685
  */
1485
1686
  live?: boolean;
1687
+ /**
1688
+ * @description `test` for a test store: its orders are paid by the test-pay call instead of a wallet, nothing is sent
1689
+ * on chain and no fee is taken. Unrelated to `live` above.
1690
+ * @enum {string}
1691
+ */
1692
+ mode?: "live" | "test";
1486
1693
  /** Format: date-time */
1487
1694
  createdAt?: string;
1488
1695
  /**
@@ -1887,13 +2094,24 @@ export interface components {
1887
2094
  refundedAt?: string | null;
1888
2095
  /** @description Signed receipt (JWS) once confirmed */
1889
2096
  receipt?: string | null;
1890
- /** @description Present while pending; how to pay */
1891
- payment?: components["schemas"]["IapEvmPayment"] | components["schemas"]["IapSolanaPayment"];
2097
+ /**
2098
+ * @description Present while pending; how to pay. A test store's order carries `{"kind": "test"}`: nothing is signed,
2099
+ * and it is paid with `POST /api/v2/iap/orders/{orderId}/test-pay`.
2100
+ */
2101
+ payment?: components["schemas"]["IapEvmPayment"] | components["schemas"]["IapSolanaPayment"] | components["schemas"]["IapTestPayment"];
1892
2102
  /** @description How to check this order's payment on chain without trusting the API. EVM: call hashOrder(order) on `checkout`, then paidBy(hash) at a block `confirmations` deep; `checkout` is the contract the order was (or will be) paid through, which hashOrder is bound to. Solana: find the transfer by its `reference` key and `memo`. Absent on test orders, and on EVM orders paid before the contract was recorded. */
1893
2103
  verification?: components["schemas"]["IapEvmVerification"] | components["schemas"]["IapSolanaVerification"];
1894
2104
  /** Format: date-time */
1895
2105
  createdAt?: string;
1896
2106
  };
2107
+ /** @description The payment block of a test store's pending order. There is nothing to sign or send */
2108
+ IapTestPayment: {
2109
+ /**
2110
+ * @description discriminator enum property added by openapi-typescript
2111
+ * @enum {string}
2112
+ */
2113
+ kind: "test";
2114
+ };
1897
2115
  IapEvmVerification: {
1898
2116
  /**
1899
2117
  * @description discriminator enum property added by openapi-typescript
@@ -2149,12 +2367,18 @@ export interface components {
2149
2367
  deliveredAt?: string | null;
2150
2368
  };
2151
2369
  IapReceiptVerification: {
2370
+ /** @description True only when `status` is `valid`. Deliver on this */
2371
+ valid?: boolean;
2152
2372
  /**
2153
- * @description `revoked` once the order is refunded
2373
+ * @description `valid`: the receipt is ours and its order stands. `revoked`: a refund was recorded since.
2374
+ * `invalid`: not a receipt Chain Daddy signed; `error` says why and there is no `payload`.
2375
+ * `unknown`: the signature is ours, but this API has no record of the receipt.
2154
2376
  * @enum {string}
2155
2377
  */
2156
- status?: "valid" | "revoked";
2157
- /** @description The receipt's decoded claims */
2378
+ status?: "valid" | "revoked" | "invalid" | "unknown";
2379
+ /** @description Why the receipt is `invalid`. Absent otherwise */
2380
+ error?: string;
2381
+ /** @description The receipt's decoded claims. Absent when `status` is `invalid` */
2158
2382
  payload?: {
2159
2383
  /** @example https://chaindaddy.io */
2160
2384
  iss?: string;
@@ -2563,8 +2787,9 @@ export interface components {
2563
2787
  };
2564
2788
  /**
2565
2789
  * @description Not allowed. `IAP_PLAN_REQUIRED` when the wallet (for a store key, the wallet that opened the store)
2566
- * has neither a Developer plan nor Developer Beta program membership in good standing. Otherwise the
2567
- * caller does not manage this token page, or the store key lacks the scope.
2790
+ * cannot run a store. That takes any paid plan with the Seller Terms signed, or Developer Beta Program
2791
+ * membership in good standing. Otherwise the caller does not manage this token page, or the store key
2792
+ * lacks the scope.
2568
2793
  */
2569
2794
  IapForbidden: {
2570
2795
  headers: {
@@ -2573,13 +2798,28 @@ export interface components {
2573
2798
  content: {
2574
2799
  /**
2575
2800
  * @example {
2576
- * "error": "a Developer plan is required to run a store",
2801
+ * "error": "running a store needs a paid plan or Developer Beta Program membership (the Developer plan is in beta and free while it is): https://chaindaddy.io/_developer",
2577
2802
  * "code": "IAP_PLAN_REQUIRED"
2578
2803
  * }
2579
2804
  */
2580
2805
  "application/json": components["schemas"]["IapError"];
2581
2806
  };
2582
2807
  };
2808
+ /** @description No wallet session */
2809
+ IapUnauthorized: {
2810
+ headers: {
2811
+ [name: string]: unknown;
2812
+ };
2813
+ content: {
2814
+ /**
2815
+ * @example {
2816
+ * "error": "sign in first",
2817
+ * "code": "UNAUTHORIZED"
2818
+ * }
2819
+ */
2820
+ "application/json": components["schemas"]["IapError"];
2821
+ };
2822
+ };
2583
2823
  /** @description `AIRDROPS_DISABLED` or `IAP_DISABLED`: the feature is switched off */
2584
2824
  IapAirdropsUnavailable: {
2585
2825
  headers: {
@@ -2695,6 +2935,12 @@ export interface components {
2695
2935
  * and never sells, grants or consumes twice.
2696
2936
  */
2697
2937
  IdempotencyKey: string;
2938
+ /**
2939
+ * @description Required: a call without it answers 400 `IDEMPOTENCY_KEY_REQUIRED`. Send one value per attempt (a UUID,
2940
+ * or an id of your own such as `cart-8842-attempt-1`), up to 255 characters. Retrying with the same key
2941
+ * and the same request returns the first result and never sells, grants or consumes twice.
2942
+ */
2943
+ IdempotencyKeyRequired: string;
2698
2944
  /** @description The store's ID (a UUID) */
2699
2945
  IapStoreId: string;
2700
2946
  /** @description The SKU key, e.g. `badge.gold` */
@@ -3215,6 +3461,26 @@ export interface operations {
3215
3461
  };
3216
3462
  };
3217
3463
  };
3464
+ getIapConfig: {
3465
+ parameters: {
3466
+ query?: never;
3467
+ header?: never;
3468
+ path?: never;
3469
+ cookie?: never;
3470
+ };
3471
+ requestBody?: never;
3472
+ responses: {
3473
+ /** @description Configuration */
3474
+ 200: {
3475
+ headers: {
3476
+ [name: string]: unknown;
3477
+ };
3478
+ content: {
3479
+ "application/json": components["schemas"]["IapConfig"];
3480
+ };
3481
+ };
3482
+ };
3483
+ };
3218
3484
  getIapJwks: {
3219
3485
  parameters: {
3220
3486
  query?: never;
@@ -3271,7 +3537,7 @@ export interface operations {
3271
3537
  };
3272
3538
  };
3273
3539
  responses: {
3274
- /** @description Verified receipt */
3540
+ /** @description The check's answer, also for a receipt that is not valid (read `valid` and `status`) */
3275
3541
  200: {
3276
3542
  headers: {
3277
3543
  [name: string]: unknown;
@@ -3308,6 +3574,89 @@ export interface operations {
3308
3574
  404: components["responses"]["IapNotFound"];
3309
3575
  };
3310
3576
  };
3577
+ getIapTestStore: {
3578
+ parameters: {
3579
+ query?: never;
3580
+ header?: never;
3581
+ path: {
3582
+ /** @description The store's ID (a UUID) */
3583
+ storeId: components["parameters"]["IapStoreId"];
3584
+ };
3585
+ cookie?: never;
3586
+ };
3587
+ requestBody?: never;
3588
+ responses: {
3589
+ /** @description The test store */
3590
+ 200: {
3591
+ headers: {
3592
+ [name: string]: unknown;
3593
+ };
3594
+ content: {
3595
+ "application/json": components["schemas"]["IapStore"];
3596
+ };
3597
+ };
3598
+ 401: components["responses"]["IapUnauthorized"];
3599
+ 403: components["responses"]["IapForbidden"];
3600
+ /**
3601
+ * @description `NOT_FOUND`: no such store, or it has no test store yet (POST here to create it).
3602
+ * `TEST_MODE_DISABLED`: test stores are not available here (`testMode` in `GET /api/v2/iap/config`).
3603
+ */
3604
+ 404: {
3605
+ headers: {
3606
+ [name: string]: unknown;
3607
+ };
3608
+ content: {
3609
+ "application/json": components["schemas"]["IapError"];
3610
+ };
3611
+ };
3612
+ };
3613
+ };
3614
+ openIapTestStore: {
3615
+ parameters: {
3616
+ query?: never;
3617
+ header?: never;
3618
+ path: {
3619
+ /** @description The store's ID (a UUID) */
3620
+ storeId: components["parameters"]["IapStoreId"];
3621
+ };
3622
+ cookie?: never;
3623
+ };
3624
+ requestBody?: never;
3625
+ responses: {
3626
+ /** @description The test store the live store already had */
3627
+ 200: {
3628
+ headers: {
3629
+ [name: string]: unknown;
3630
+ };
3631
+ content: {
3632
+ "application/json": components["schemas"]["IapStore"];
3633
+ };
3634
+ };
3635
+ /** @description The test store, just created */
3636
+ 201: {
3637
+ headers: {
3638
+ [name: string]: unknown;
3639
+ };
3640
+ content: {
3641
+ "application/json": components["schemas"]["IapStore"];
3642
+ };
3643
+ };
3644
+ 401: components["responses"]["IapUnauthorized"];
3645
+ 403: components["responses"]["IapForbidden"];
3646
+ /**
3647
+ * @description `NOT_FOUND`: no such store. `TEST_MODE_DISABLED`: test stores are not available here (`testMode` in
3648
+ * `GET /api/v2/iap/config`).
3649
+ */
3650
+ 404: {
3651
+ headers: {
3652
+ [name: string]: unknown;
3653
+ };
3654
+ content: {
3655
+ "application/json": components["schemas"]["IapError"];
3656
+ };
3657
+ };
3658
+ };
3659
+ };
3311
3660
  listIapSkus: {
3312
3661
  parameters: {
3313
3662
  query?: never;
@@ -3577,12 +3926,13 @@ export interface operations {
3577
3926
  grantIapItems: {
3578
3927
  parameters: {
3579
3928
  query?: never;
3580
- header?: {
3929
+ header: {
3581
3930
  /**
3582
- * @description Send a unique value (a UUID) with every POST. Retrying with the same key returns the first result
3583
- * and never sells, grants or consumes twice.
3931
+ * @description Required: a call without it answers 400 `IDEMPOTENCY_KEY_REQUIRED`. Send one value per attempt (a UUID,
3932
+ * or an id of your own such as `cart-8842-attempt-1`), up to 255 characters. Retrying with the same key
3933
+ * and the same request returns the first result and never sells, grants or consumes twice.
3584
3934
  */
3585
- "Idempotency-Key"?: components["parameters"]["IdempotencyKey"];
3935
+ "Idempotency-Key": components["parameters"]["IdempotencyKeyRequired"];
3586
3936
  };
3587
3937
  path: {
3588
3938
  /** @description The store's ID (a UUID) */
@@ -3617,12 +3967,13 @@ export interface operations {
3617
3967
  consumeIapItems: {
3618
3968
  parameters: {
3619
3969
  query?: never;
3620
- header?: {
3970
+ header: {
3621
3971
  /**
3622
- * @description Send a unique value (a UUID) with every POST. Retrying with the same key returns the first result
3623
- * and never sells, grants or consumes twice.
3972
+ * @description Required: a call without it answers 400 `IDEMPOTENCY_KEY_REQUIRED`. Send one value per attempt (a UUID,
3973
+ * or an id of your own such as `cart-8842-attempt-1`), up to 255 characters. Retrying with the same key
3974
+ * and the same request returns the first result and never sells, grants or consumes twice.
3624
3975
  */
3625
- "Idempotency-Key"?: components["parameters"]["IdempotencyKey"];
3976
+ "Idempotency-Key": components["parameters"]["IdempotencyKeyRequired"];
3626
3977
  };
3627
3978
  path: {
3628
3979
  /** @description The store's ID (a UUID) */
@@ -3940,12 +4291,13 @@ export interface operations {
3940
4291
  awardIapDrop: {
3941
4292
  parameters: {
3942
4293
  query?: never;
3943
- header?: {
4294
+ header: {
3944
4295
  /**
3945
- * @description Send a unique value (a UUID) with every POST. Retrying with the same key returns the first result
3946
- * and never sells, grants or consumes twice.
4296
+ * @description Required: a call without it answers 400 `IDEMPOTENCY_KEY_REQUIRED`. Send one value per attempt (a UUID,
4297
+ * or an id of your own such as `cart-8842-attempt-1`), up to 255 characters. Retrying with the same key
4298
+ * and the same request returns the first result and never sells, grants or consumes twice.
3947
4299
  */
3948
- "Idempotency-Key"?: components["parameters"]["IdempotencyKey"];
4300
+ "Idempotency-Key": components["parameters"]["IdempotencyKeyRequired"];
3949
4301
  };
3950
4302
  path: {
3951
4303
  /** @description The store's ID (a UUID) */
@@ -4497,12 +4849,13 @@ export interface operations {
4497
4849
  createIapOrder: {
4498
4850
  parameters: {
4499
4851
  query?: never;
4500
- header?: {
4852
+ header: {
4501
4853
  /**
4502
- * @description Send a unique value (a UUID) with every POST. Retrying with the same key returns the first result
4503
- * and never sells, grants or consumes twice.
4854
+ * @description Required: a call without it answers 400 `IDEMPOTENCY_KEY_REQUIRED`. Send one value per attempt (a UUID,
4855
+ * or an id of your own such as `cart-8842-attempt-1`), up to 255 characters. Retrying with the same key
4856
+ * and the same request returns the first result and never sells, grants or consumes twice.
4504
4857
  */
4505
- "Idempotency-Key"?: components["parameters"]["IdempotencyKey"];
4858
+ "Idempotency-Key": components["parameters"]["IdempotencyKeyRequired"];
4506
4859
  };
4507
4860
  path?: never;
4508
4861
  cookie?: never;
@@ -4635,15 +4988,79 @@ export interface operations {
4635
4988
  404: components["responses"]["IapNotFound"];
4636
4989
  };
4637
4990
  };
4991
+ testPayIapOrder: {
4992
+ parameters: {
4993
+ query?: never;
4994
+ header?: never;
4995
+ path: {
4996
+ /** @description `0x` followed by 64 hex characters */
4997
+ orderId: components["parameters"]["IapOrderId"];
4998
+ };
4999
+ cookie?: never;
5000
+ };
5001
+ requestBody?: never;
5002
+ responses: {
5003
+ /** @description The order, confirmed, with its receipt */
5004
+ 200: {
5005
+ headers: {
5006
+ [name: string]: unknown;
5007
+ };
5008
+ content: {
5009
+ "application/json": components["schemas"]["IapOrder"];
5010
+ };
5011
+ };
5012
+ /** @description No credential, or a store key that is unknown or revoked (`INVALID_STORE_KEY`) */
5013
+ 401: {
5014
+ headers: {
5015
+ [name: string]: unknown;
5016
+ };
5017
+ content: {
5018
+ "application/json": components["schemas"]["IapError"];
5019
+ };
5020
+ };
5021
+ 403: components["responses"]["IapForbidden"];
5022
+ /**
5023
+ * @description `NOT_FOUND`: no such order, or it is not an order of the caller's store or coin page.
5024
+ * `TEST_MODE_DISABLED`: test stores are not available here (`testMode` in `GET /api/v2/iap/config`).
5025
+ */
5026
+ 404: {
5027
+ headers: {
5028
+ [name: string]: unknown;
5029
+ };
5030
+ content: {
5031
+ "application/json": components["schemas"]["IapError"];
5032
+ };
5033
+ };
5034
+ /**
5035
+ * @description `LIVE_ORDER`: the order belongs to a live store. `ORDER_EXPIRED`: the order's deadline has passed;
5036
+ * create a new one.
5037
+ */
5038
+ 409: {
5039
+ headers: {
5040
+ [name: string]: unknown;
5041
+ };
5042
+ content: {
5043
+ /**
5044
+ * @example {
5045
+ * "error": "this is a live order: it is paid on chain, and test-pay pays test orders only",
5046
+ * "code": "LIVE_ORDER"
5047
+ * }
5048
+ */
5049
+ "application/json": components["schemas"]["IapError"];
5050
+ };
5051
+ };
5052
+ };
5053
+ };
4638
5054
  createIapCheckoutSession: {
4639
5055
  parameters: {
4640
5056
  query?: never;
4641
- header?: {
5057
+ header: {
4642
5058
  /**
4643
- * @description Send a unique value (a UUID) with every POST. Retrying with the same key returns the first result
4644
- * and never sells, grants or consumes twice.
5059
+ * @description Required: a call without it answers 400 `IDEMPOTENCY_KEY_REQUIRED`. Send one value per attempt (a UUID,
5060
+ * or an id of your own such as `cart-8842-attempt-1`), up to 255 characters. Retrying with the same key
5061
+ * and the same request returns the first result and never sells, grants or consumes twice.
4645
5062
  */
4646
- "Idempotency-Key"?: components["parameters"]["IdempotencyKey"];
5063
+ "Idempotency-Key": components["parameters"]["IdempotencyKeyRequired"];
4647
5064
  };
4648
5065
  path?: never;
4649
5066
  cookie?: never;
package/dist/cd.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";(()=>{var S="0.3.1";function v(t){let e="";for(let r of t)e+=String.fromCharCode(r);return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function f(){return v(crypto.getRandomValues(new Uint8Array(32)))}function R(t,e,r,s,o){let d=n=>{if(n&&typeof n=="object"&&n.type===t)try{r(n)}catch{}},u=n=>{n.origin===e&&(!o||n.source===o)&&d(n.data)};window.addEventListener("message",u);let c=null;if(s&&typeof BroadcastChannel=="function"&&e===location.origin)try{c=new BroadcastChannel(s),c.onmessage=n=>d(n.data)}catch{c=null}return()=>{window.removeEventListener("message",u),c?.close()}}var m={get(t){try{return sessionStorage.getItem(t)}catch{return null}},set(t,e){try{sessionStorage.setItem(t,e)}catch{}},remove(t){try{sessionStorage.removeItem(t)}catch{}}};var q="https://api.chaindaddy.io",V={"https://api.chaindaddy.io":"https://chaindaddy.io/_oauth/authorize","https://staging-api.chaindaddy.io":"https://staging.chaindaddy.io/_oauth/authorize"},w="cd-signin",T="cd:signin",y="cd.signin";async function F(t){let e=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(t));return v(new Uint8Array(e))}function G(t="openid"){let e=t.split(/\s+/).filter(Boolean);return(e.includes("openid")?e:["openid",...e]).join(" ")}async function K(t,e,r){let s=new URLSearchParams({response_type:"code",client_id:e.clientId,redirect_uri:e.redirectUri,scope:G(t.scope),state:e.state,nonce:e.nonce,code_challenge:await F(e.verifier),code_challenge_method:"S256"});return t.claims&&s.set("claims",typeof t.claims=="string"?t.claims:JSON.stringify(t.claims)),`${r}?${s}`}function _(t,e){return{code:e,codeVerifier:t.verifier,state:t.state,nonce:t.nonce,issuer:t.issuer,clientId:t.clientId,redirectUri:t.redirectUri}}function b(t){let e=(t.issuer??q).replace(/\/$/,""),r=t.authorizeUrl??V[e];if(!r)return Promise.reject(new Error("authorizeUrl is required for this issuer"));if(!t.clientId||!t.redirectUri)return Promise.reject(new Error("clientId and redirectUri are required"));let s={state:f(),nonce:f(),verifier:f(),issuer:e,clientId:t.clientId,redirectUri:t.redirectUri};m.set(y,JSON.stringify(s));let o=window.open("",w,"popup,width=480,height=720");return(async()=>{let d=await K(t,s,r);if(!o)return location.assign(d),null;o.location.href=d;let u=new URL(t.redirectUri,location.href).origin;return new Promise((c,n)=>{let a=R(T,u,i=>{i.state===s.state&&(a(),m.remove(y),typeof i.error=="string"&&i.error?n(new Error(i.error)):i.iss!==e?n(new Error("the answer came from another issuer")):typeof i.code!="string"||!i.code?n(new Error("the answer carried no code")):c(_(s,i.code)))},w,o);t.signal?.addEventListener("abort",()=>{a(),n(new Error("aborted"))})})})()}async function U(){let t=new URLSearchParams(location.search),e={type:T,code:t.get("code"),state:t.get("state"),iss:t.get("iss"),error:t.get("error")};if(window.name===w){try{window.opener?.postMessage(e,location.origin)}catch{}try{let o=new BroadcastChannel(w);o.postMessage(e),o.close()}catch{}return window.close(),null}let r=m.get(y);m.remove(y);let s=r?JSON.parse(r):null;if(!s||e.state!==s.state)throw new Error("the sign-in state does not match: start again");if(e.error)throw new Error(e.error);if(e.iss!==s.issuer)throw new Error("the answer came from another issuer");if(!e.code)throw new Error("the answer carried no code");return _(s,e.code)}async function A(t){let e=await fetch(`${t.issuer}/api/v2/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:t.code,redirect_uri:t.redirectUri,client_id:t.clientId,code_verifier:t.codeVerifier})}),r=await e.json();if(!e.ok)throw new Error(`${r.error??e.status}: ${r.error_description??""}`.trim());return r}var W='<svg viewBox="0 0 24 24" fill="#FFD700" width="20" height="20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><path d="M5 16L3 5l5.5 5L12 4l3.5 6L21 5l-2 11H5z" stroke="#B8860B" stroke-width="0.5"/><path d="M5 18h14v2H5v-2z" stroke="#B8860B" stroke-width="0.5"/></svg>',Y=`
1
+ "use strict";(()=>{var S="0.3.2";function k(t){let e="";for(let i of t)e+=String.fromCharCode(i);return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function f(){return k(crypto.getRandomValues(new Uint8Array(32)))}function R(t,e,i,s,o){let u=n=>{if(n&&typeof n=="object"&&n.type===t)try{i(n)}catch{}},l=n=>{n.origin===e&&(!o||n.source===o)&&u(n.data)};window.addEventListener("message",l);let a=null;if(s&&typeof BroadcastChannel=="function"&&e===location.origin)try{a=new BroadcastChannel(s),a.onmessage=n=>u(n.data)}catch{a=null}return()=>{window.removeEventListener("message",l),a?.close()}}var m={get(t){try{return sessionStorage.getItem(t)}catch{return null}},set(t,e){try{sessionStorage.setItem(t,e)}catch{}},remove(t){try{sessionStorage.removeItem(t)}catch{}}};var q="https://api.chaindaddy.io",V={"https://api.chaindaddy.io":"https://chaindaddy.io/_oauth/authorize","https://staging-api.chaindaddy.io":"https://staging.chaindaddy.io/_oauth/authorize"},w="cd-signin",T="cd:signin",y="cd.signin";async function F(t){let e=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(t));return k(new Uint8Array(e))}function G(t="openid"){let e=t.split(/\s+/).filter(Boolean);return(e.includes("openid")?e:["openid",...e]).join(" ")}async function K(t,e,i){let s=new URLSearchParams({response_type:"code",client_id:e.clientId,redirect_uri:e.redirectUri,scope:G(t.scope),state:e.state,nonce:e.nonce,code_challenge:await F(e.verifier),code_challenge_method:"S256"});return t.claims&&s.set("claims",typeof t.claims=="string"?t.claims:JSON.stringify(t.claims)),`${i}?${s}`}function U(t,e){return{code:e,codeVerifier:t.verifier,state:t.state,nonce:t.nonce,issuer:t.issuer,clientId:t.clientId,redirectUri:t.redirectUri}}function b(t){let e=(t.issuer??q).replace(/\/$/,""),i=t.authorizeUrl??V[e];if(!i)return Promise.reject(new Error("authorizeUrl is required for this issuer"));if(!t.clientId||!t.redirectUri)return Promise.reject(new Error("clientId and redirectUri are required"));let s={state:f(),nonce:f(),verifier:f(),issuer:e,clientId:t.clientId,redirectUri:t.redirectUri};m.set(y,JSON.stringify(s));let o=window.open("",w,"popup,width=480,height=720");return(async()=>{let u=await K(t,s,i);if(!o)return location.assign(u),null;o.location.href=u;let l=new URL(t.redirectUri,location.href).origin;return new Promise((a,n)=>{let c=R(T,l,r=>{r.state===s.state&&(c(),m.remove(y),typeof r.error=="string"&&r.error?n(new Error(r.error)):r.iss!==e?n(new Error("the answer came from another issuer")):typeof r.code!="string"||!r.code?n(new Error("the answer carried no code")):a(U(s,r.code)))},w,o);t.signal?.addEventListener("abort",()=>{c(),n(new Error("aborted"))})})})()}async function _(){let t=new URLSearchParams(location.search),e={type:T,code:t.get("code"),state:t.get("state"),iss:t.get("iss"),error:t.get("error")};if(window.name===w){try{window.opener?.postMessage(e,location.origin)}catch{}try{let o=new BroadcastChannel(w);o.postMessage(e),o.close()}catch{}return window.close(),null}let i=m.get(y);m.remove(y);let s=i?JSON.parse(i):null;if(!s||e.state!==s.state)throw new Error("the sign-in state does not match: start again");if(e.error)throw new Error(e.error);if(e.iss!==s.issuer)throw new Error("the answer came from another issuer");if(!e.code)throw new Error("the answer carried no code");return U(s,e.code)}async function A(t){let e=await fetch(`${t.issuer}/api/v2/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:t.code,redirect_uri:t.redirectUri,client_id:t.clientId,code_verifier:t.codeVerifier})}),i=await e.json();if(!e.ok)throw new Error(`${i.error??e.status}: ${i.error_description??""}`.trim());return i}var W='<svg viewBox="0 0 24 24" fill="#FFD700" width="20" height="20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><path d="M5 16L3 5l5.5 5L12 4l3.5 6L21 5l-2 11H5z" stroke="#B8860B" stroke-width="0.5"/><path d="M5 18h14v2H5v-2z" stroke="#B8860B" stroke-width="0.5"/></svg>',Y=`
2
2
  :host { display: inline-block; }
3
3
  button { display: inline-flex; align-items: center; justify-content: center; gap: 10px; box-sizing: border-box;
4
4
  min-height: 40px; padding: 0 16px; border-radius: 10px; font: 600 15px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
@@ -7,16 +7,16 @@ button[data-theme="dark"] { background: #101828; color: #ffffff; border-color: #
7
7
  button:focus-visible { outline: 2px solid #f59e0b; outline-offset: 2px; }
8
8
  button:disabled { opacity: .6; cursor: default; }
9
9
  svg { flex: none; }
10
- `;function L(){if(typeof customElements>"u"||customElements.get("cd-signin-button"))return;class t extends HTMLElement{constructor(){super(),this.root=this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(){this.isConnected&&this.render()}render(){let r=this.getAttribute("text")==="continue"?"Continue with Chain Daddy":"Sign in with Chain Daddy",s=this.getAttribute("theme")==="dark"?"dark":"light";this.root.innerHTML=`<style>${Y}</style><button type="button" part="button" data-theme="${s}">${W}<span></span></button>`;let o=this.root.querySelector("button");o.querySelector("span").textContent=r,o.addEventListener("click",()=>this.start())}start(){let r=s=>this.getAttribute(s)||void 0;b({clientId:r("client-id")??"",redirectUri:r("redirect-uri")??"",scope:r("scope"),claims:r("claims"),issuer:r("issuer"),authorizeUrl:r("authorize-url")}).then(s=>s&&this.dispatchEvent(new CustomEvent("cd-signin",{detail:s,bubbles:!0,composed:!0})),s=>this.dispatchEvent(new CustomEvent("cd-signin-error",{detail:{message:String(s?.message??s)},bubbles:!0,composed:!0})))}}t.observedAttributes=["theme","text"],customElements.define("cd-signin-button",t)}var M="https://embed.chaindaddy.io",k="https://embed-staging.chaindaddy.io";var J=["--cd-accent","--cd-surface","--cd-text","--cd-text-muted","--cd-border"];function P(t){try{if(t&&new URL(t).hostname==="js.staging.chaindaddy.io")return k}catch{}return M}var X=`
10
+ `;function L(){if(typeof customElements>"u"||customElements.get("cd-signin-button"))return;class t extends HTMLElement{constructor(){super(),this.root=this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(){this.isConnected&&this.render()}render(){let i=this.getAttribute("text")==="continue"?"Continue with Chain Daddy":"Sign in with Chain Daddy",s=this.getAttribute("theme")==="dark"?"dark":"light";this.root.innerHTML=`<style>${Y}</style><button type="button" part="button" data-theme="${s}">${W}<span></span></button>`;let o=this.root.querySelector("button");o.querySelector("span").textContent=i,o.addEventListener("click",()=>this.start())}start(){let i=s=>this.getAttribute(s)||void 0;b({clientId:i("client-id")??"",redirectUri:i("redirect-uri")??"",scope:i("scope"),claims:i("claims"),issuer:i("issuer"),authorizeUrl:i("authorize-url")}).then(s=>s&&this.dispatchEvent(new CustomEvent("cd-signin",{detail:s,bubbles:!0,composed:!0})),s=>this.dispatchEvent(new CustomEvent("cd-signin-error",{detail:{message:String(s?.message??s)},bubbles:!0,composed:!0})))}}t.observedAttributes=["theme","text"],customElements.define("cd-signin-button",t)}var M="https://embed.chaindaddy.io",v="https://embed-staging.chaindaddy.io";var J="1500px 0px",X="docs.chaindaddy.io/token/apps-on-your-site#if-the-link-stays";var Q=["--cd-accent","--cd-surface","--cd-text","--cd-text-muted","--cd-border"];function O(t){try{if(t&&new URL(t).hostname==="js.staging.chaindaddy.io")return v}catch{}return M}var Z=`
11
11
  :host { display: block; }
12
12
  iframe { display: block; width: 100%; border: 0; height: 1px; opacity: 0; color-scheme: normal; }
13
13
  :host([data-cd-ready]) iframe { opacity: 1; }
14
14
  :host([data-cd-ready]) slot { display: none; }
15
15
  :host([data-cd-unavailable]) iframe { display: none; }
16
- `,Q=`
16
+ `,tt=`
17
17
  :host { display: inline-block; }
18
18
  button { box-sizing: border-box; min-height: 40px; padding: 0 16px; border-radius: 10px; cursor: pointer;
19
19
  font: 600 15px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
20
20
  border: 1px solid var(--cd-border, #d0d5dd); background: var(--cd-accent, #f59e0b); color: var(--cd-text, #101828); }
21
21
  button:focus-visible { outline: 2px solid var(--cd-accent, #f59e0b); outline-offset: 2px; }
22
- `;function O(t={}){try{if(typeof customElements>"u"||typeof HTMLElement>"u")return;let e=t.origin===k?k:M;class r extends HTMLElement{constructor(){super(...arguments);this.kind="app";this.frame=null;this.onMessage=n=>this.receive(n)}connectedCallback(){try{this.shadowRoot||this.attachShadow({mode:"open"}),this.mount()}catch{this.unavailable()}}disconnectedCallback(){try{window.removeEventListener("message",this.onMessage),clearTimeout(this.timer)}catch{}}attributeChangedCallback(){try{this.isConnected&&this.shadowRoot&&this.mount()}catch{this.unavailable()}}src(){let n=(this.getAttribute("crown")??"").trim(),a=(this.getAttribute("chain")??"").trim(),i=(this.getAttribute("app")??"").trim();if(!/^[0-9]{1,18}$/.test(n)||!a||this.kind==="app"&&!i)return null;let l=new URLSearchParams({crown:n,chain:a});return this.kind==="app"?l.set("app",i):l.set("kind","coin"),l.set("mode",this.mode()),l.set("v","1"),`${e}/v1/frame?${l.toString()}`}mode(){let n=this.getAttribute("mode");if(n==="light"||n==="dark")return n;try{return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}catch{return"light"}}mount(){let n=this.shadowRoot;window.removeEventListener("message",this.onMessage),clearTimeout(this.timer),this.removeAttribute("data-cd-ready"),this.removeAttribute("data-cd-unavailable");let a=this.src();if(!a){n.innerHTML="<slot></slot>",this.frame=null;return}n.innerHTML=`<style>${X}</style><slot></slot>`;let i=document.createElement("iframe");i.setAttribute("loading","lazy"),i.setAttribute("referrerpolicy","strict-origin-when-cross-origin"),i.setAttribute("allow","clipboard-write; fullscreen"),i.setAttribute("title",this.kind==="coin"?"Coin on Chain Daddy":"App on Chain Daddy"),i.src=a,n.appendChild(i),this.frame=i,window.addEventListener("message",this.onMessage),this.timer=setTimeout(()=>{this.hasAttribute("data-cd-ready")||this.unavailable()},8e3)}receive(n){try{if(n.origin!==e||!this.frame||!n.source||n.source!==this.frame.contentWindow)return;let a=n.data;if(!a||typeof a!="object")return;if(a.type==="cd:ready")clearTimeout(this.timer),this.removeAttribute("data-cd-unavailable"),this.setAttribute("data-cd-ready",""),this.sendTheme(),this.dispatchEvent(new CustomEvent("cd:ready"));else if(a.type==="cd:resize"){let i=Number(a.height);Number.isFinite(i)&&(this.frame.style.height=`${Math.max(1,Math.min(4e3,Math.ceil(i)))}px`)}else a.type==="cd:unavailable"&&this.unavailable()}catch{}}sendTheme(){let n={},a=getComputedStyle(this);for(let i of J){let l=a.getPropertyValue(i).trim();l&&(n[i]=l)}this.frame?.contentWindow?.postMessage({type:"cd:theme",tokens:n},e)}unavailable(){try{if(clearTimeout(this.timer),this.removeAttribute("data-cd-ready"),this.hasAttribute("data-cd-unavailable"))return;this.setAttribute("data-cd-unavailable",""),this.dispatchEvent(new CustomEvent("cd:unavailable"))}catch{}}}r.observedAttributes=["crown","chain","app","mode"];class s extends r{}class o extends r{constructor(){super(...arguments);this.kind="coin"}}class d extends HTMLElement{connectedCallback(){try{let c=this.querySelector("a[href]"),n=c?new URL(c.getAttribute("href")??"",document.baseURI):null;if(!n||n.protocol!=="https:")return;let a=this.shadowRoot??this.attachShadow({mode:"open"});a.innerHTML=`<style>${Q}</style><button type="button" part="button"></button>`;let i=a.querySelector("button");i.textContent=(c.textContent??"").trim()||"Buy on Chain Daddy",i.addEventListener("click",()=>{try{window.open(n.toString(),"cd-buy","popup,width=460,height=760")||window.location.assign(n.toString())}catch{}})}catch{}}}for(let[u,c]of[["cd-app",s],["cd-coin",o],["cd-buy",d]])customElements.get(u)||customElements.define(u,c)}catch{}}var Z=["https://chaindaddy.io","https://staging.chaindaddy.io"],D=()=>"cd-checkout-"+f(),H="popup,width=480,height=820",$=300,tt=7;function E(t){if(typeof t!="string")return null;let e;try{e=new URL(t)}catch{return null}let r=/^\/_pay\/([\w-]+)$/.exec(e.pathname);return r&&Z.includes(e.origin)&&!e.username&&!e.password?{href:e.origin+e.pathname,origin:e.origin,id:r[1]}:null}function C(t,e){if(typeof t!="string")return null;let r;try{r=new URL(t,location.href)}catch{return null}if(!/^https?:$/.test(r.protocol)||r.username||r.password)return null;let s=d=>{let u=d.split("=")[0];try{return decodeURIComponent(u.replace(/\+/g," "))}catch{return u}},o=r.search.slice(1).split("&").filter(d=>d&&s(d)!=="session_id");return o.push("session_id="+encodeURIComponent(e)),r.origin+r.pathname+"?"+o.join("&")+r.hash}var p;function N(t,e,r){return new Promise(s=>{let o=!1,d=0,u,c=l=>{clearInterval(i),clearTimeout(u),window.removeEventListener("message",n),r&&l.status==="completed"&&location.assign(r),s(l)},n=l=>{if(l.origin!==e.origin||l.source!==t)return;let g=l.data;if(!g||typeof g!="object")return;g.type==="cd:checkout.ready"&&(o=!0);let h=g.session;if(!(!h||typeof h!="object"||h.id!==e.id)){if(g.type==="cd:checkout.cancelled")c({status:"cancelled",session:{id:e.id}});else if(g.type==="cd:checkout.completed"&&(h.status==="complete"||h.status==="paid")){let I={id:e.id,status:h.status};typeof h.orderId=="string"&&/^[\w-]{1,128}$/.test(h.orderId)&&(I.orderId=h.orderId),c({status:"completed",session:I})}}},a=()=>{try{t.postMessage({type:"cd:checkout.hello"},e.origin)}catch{}},i=setInterval(()=>{t.closed?(clearInterval(i),u=setTimeout(()=>c({status:"closed"}),$)):(!o||++d%tt===0)&&a()},$);window.addEventListener("message",n),a()})}function x(t){let e=()=>new Error("checkout: url is not a Chain Daddy checkout session link"),r=(o,d,u)=>{p={popup:o,result:d,href:u};let c=()=>{p&&p.popup===o&&(p=void 0)};return d.then(c,c),d},s=o=>(location.assign(o.href),{status:"redirected"});try{let{url:o,successUrl:d}=t;if(d&&!C(d,""))throw new Error("checkout: successUrl is not a web address");let u=i=>d?C(d,i.id):null,c=typeof o=="function"?null:E(o);if(typeof o!="function"&&!c)throw e();if(p&&!p.popup.closed&&(!c||p.href===c.href))return p.popup.focus(),p.result;if(c){let i=window.open(c.href,D(),H);return i?r(i,N(i,c,u(c)),c.href):Promise.resolve(s(c))}let n=window.open("",D(),H),a=new Promise(i=>i(o())).then(i=>{let l=E(i);if(!l)throw n?.close(),e();return n?(n.location.href=l.href,N(n,l,u(l))):s(l)},i=>{throw n?.close(),i});return n?r(n,a):a}catch(o){return Promise.reject(o)}}var B=!1,z;function j(){B||typeof document>"u"||(B=!0,document.addEventListener("click",t=>{let e=t.target instanceof Element?t.target.closest("[data-cd-checkout]"):null;if(!e||t.defaultPrevented||t.button||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)return;let r=e.getAttribute("data-cd-checkout")||e.href,s=e.getAttribute("data-cd-success");if(!E(r)||s&&!C(s,""))return;t.preventDefault();let o=x({url:r,successUrl:s});o!==z&&(z=o,o.then(d=>void e.dispatchEvent(new CustomEvent("cd:checkout",{detail:d,bubbles:!0})),()=>{}))}))}var et={version:S,signIn:b,handleCallback:U,exchangeCode:A,checkout:x};window.ChainDaddy=Object.assign(window.ChainDaddy??{},et);L();O({origin:P(document.currentScript?.src)});j();})();
22
+ `;function P(t={}){try{if(typeof customElements>"u"||typeof HTMLElement>"u")return;let e=t.origin===v?v:M;class i extends HTMLElement{constructor(){super(...arguments);this.kind="app";this.frame=null;this.onMessage=n=>this.receive(n)}connectedCallback(){try{this.shadowRoot||this.attachShadow({mode:"open"}),this.mount()}catch{this.unavailable()}}disconnectedCallback(){try{window.removeEventListener("message",this.onMessage),clearTimeout(this.timer),this.watch?.disconnect()}catch{}}attributeChangedCallback(){try{this.isConnected&&this.shadowRoot&&this.mount()}catch{this.unavailable()}}src(){let n=(this.getAttribute("crown")??"").trim(),c=(this.getAttribute("chain")??"").trim(),r=(this.getAttribute("app")??"").trim();if(!/^[0-9]{1,18}$/.test(n)||!c||this.kind==="app"&&!r)return null;let d=new URLSearchParams({crown:n,chain:c});return this.kind==="app"?d.set("app",r):d.set("kind","coin"),d.set("mode",this.mode()),d.set("v","1"),`${e}/v1/frame?${d.toString()}`}mode(){let n=this.getAttribute("mode");if(n==="light"||n==="dark")return n;try{return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}catch{return"light"}}mount(){let n=this.shadowRoot;window.removeEventListener("message",this.onMessage),clearTimeout(this.timer),this.watch?.disconnect(),this.removeAttribute("data-cd-ready"),this.removeAttribute("data-cd-unavailable");let c=this.src();if(!c){n.innerHTML="<slot></slot>",this.frame=null;return}n.innerHTML=`<style>${Z}</style><slot></slot>`;let r=document.createElement("iframe");r.setAttribute("referrerpolicy","strict-origin-when-cross-origin"),r.setAttribute("allow","clipboard-write; fullscreen"),r.setAttribute("title",this.kind==="coin"?"Coin on Chain Daddy":"App on Chain Daddy"),n.appendChild(r),this.frame=r,window.addEventListener("message",this.onMessage);let d=()=>{r.src=c,this.timer=setTimeout(()=>{this.hasAttribute("data-cd-ready")||this.unavailable()},8e3)};if(typeof IntersectionObserver>"u")return d();this.watch=new IntersectionObserver(g=>{g.some(h=>h.isIntersecting)&&(this.watch?.disconnect(),d())},{rootMargin:J}),this.watch.observe(this)}why(){try{let n=new URL(this.frame.src).searchParams,c=n.get("app");n.delete("mode"),n.delete("v"),fetch(`https://${e===v?"staging-api":"api"}.chaindaddy.io/api/v2/embed/status?${n}`,{credentials:"omit"}).then(r=>r.json()).then(r=>{let d=r.embeddable?r.interactive?"not a verified site of the coin":"not https, or the app did not start":r.reason;console.info(`[Chain Daddy] cd-${this.kind} ${c??""} cannot show here: ${d}. ${X}`)}).catch(()=>{})}catch{}}receive(n){try{if(n.origin!==e||!this.frame||!n.source||n.source!==this.frame.contentWindow)return;let c=n.data;if(!c||typeof c!="object")return;if(c.type==="cd:ready")clearTimeout(this.timer),this.removeAttribute("data-cd-unavailable"),this.setAttribute("data-cd-ready",""),this.sendTheme(),this.dispatchEvent(new CustomEvent("cd:ready"));else if(c.type==="cd:resize"){let r=Number(c.height);Number.isFinite(r)&&(this.frame.style.height=`${Math.max(1,Math.min(4e3,Math.ceil(r)))}px`)}else c.type==="cd:unavailable"&&this.unavailable()}catch{}}sendTheme(){let n={},c=getComputedStyle(this);for(let r of Q){let d=c.getPropertyValue(r).trim();d&&(n[r]=d)}this.frame?.contentWindow?.postMessage({type:"cd:theme",tokens:n},e)}unavailable(){try{if(clearTimeout(this.timer),this.removeAttribute("data-cd-ready"),this.hasAttribute("data-cd-unavailable"))return;this.setAttribute("data-cd-unavailable",""),this.dispatchEvent(new CustomEvent("cd:unavailable")),this.why()}catch{}}}i.observedAttributes=["crown","chain","app","mode"];class s extends i{}class o extends i{constructor(){super(...arguments);this.kind="coin"}}class u extends HTMLElement{connectedCallback(){try{let a=this.querySelector("a[href]"),n=a?new URL(a.getAttribute("href")??"",document.baseURI):null;if(!n||n.protocol!=="https:")return;let c=this.shadowRoot??this.attachShadow({mode:"open"});c.innerHTML=`<style>${tt}</style><button type="button" part="button"></button>`;let r=c.querySelector("button");r.textContent=(a.textContent??"").trim()||"Buy on Chain Daddy",r.addEventListener("click",()=>{try{window.open(n.toString(),"cd-buy","popup,width=460,height=760")||window.location.assign(n.toString())}catch{}})}catch{}}}for(let[l,a]of[["cd-app",s],["cd-coin",o],["cd-buy",u]])customElements.get(l)||customElements.define(l,a)}catch{}}var et=["https://chaindaddy.io","https://staging.chaindaddy.io"],$=()=>"cd-checkout-"+f(),D="popup,width=480,height=820",H=300,nt=7;function E(t){if(typeof t!="string")return null;let e;try{e=new URL(t)}catch{return null}let i=/^\/_pay\/([\w-]+)$/.exec(e.pathname);return i&&et.includes(e.origin)&&!e.username&&!e.password?{href:e.origin+e.pathname,origin:e.origin,id:i[1]}:null}function C(t,e){if(typeof t!="string")return null;let i;try{i=new URL(t,location.href)}catch{return null}if(!/^https?:$/.test(i.protocol)||i.username||i.password)return null;let s=u=>{let l=u.split("=")[0];try{return decodeURIComponent(l.replace(/\+/g," "))}catch{return l}},o=i.search.slice(1).split("&").filter(u=>u&&s(u)!=="session_id");return o.push("session_id="+encodeURIComponent(e)),i.origin+i.pathname+"?"+o.join("&")+i.hash}var p;function N(t,e,i){return new Promise(s=>{let o=!1,u=0,l,a=d=>{clearInterval(r),clearTimeout(l),window.removeEventListener("message",n),i&&d.status==="completed"&&location.assign(i),s(d)},n=d=>{if(d.origin!==e.origin||d.source!==t)return;let g=d.data;if(!g||typeof g!="object")return;g.type==="cd:checkout.ready"&&(o=!0);let h=g.session;if(!(!h||typeof h!="object"||h.id!==e.id)){if(g.type==="cd:checkout.cancelled")a({status:"cancelled",session:{id:e.id}});else if(g.type==="cd:checkout.completed"&&(h.status==="complete"||h.status==="paid")){let x={id:e.id,status:h.status};typeof h.orderId=="string"&&/^[\w-]{1,128}$/.test(h.orderId)&&(x.orderId=h.orderId),a({status:"completed",session:x})}}},c=()=>{try{t.postMessage({type:"cd:checkout.hello"},e.origin)}catch{}},r=setInterval(()=>{t.closed?(clearInterval(r),l=setTimeout(()=>a({status:"closed"}),H)):(!o||++u%nt===0)&&c()},H);window.addEventListener("message",n),c()})}function I(t){let e=()=>new Error("checkout: url is not a Chain Daddy checkout session link"),i=(o,u,l)=>{p={popup:o,result:u,href:l};let a=()=>{p&&p.popup===o&&(p=void 0)};return u.then(a,a),u},s=o=>(location.assign(o.href),{status:"redirected"});try{let{url:o,successUrl:u}=t;if(u&&!C(u,""))throw new Error("checkout: successUrl is not a web address");let l=r=>u?C(u,r.id):null,a=typeof o=="function"?null:E(o);if(typeof o!="function"&&!a)throw e();if(p&&!p.popup.closed&&(!a||p.href===a.href))return p.popup.focus(),p.result;if(a){let r=window.open(a.href,$(),D);return r?i(r,N(r,a,l(a)),a.href):Promise.resolve(s(a))}let n=window.open("",$(),D),c=new Promise(r=>r(o())).then(r=>{let d=E(r);if(!d)throw n?.close(),e();return n?(n.location.href=d.href,N(n,d,l(d))):s(d)},r=>{throw n?.close(),r});return n?i(n,c):c}catch(o){return Promise.reject(o)}}var B=!1,z;function j(){B||typeof document>"u"||(B=!0,document.addEventListener("click",t=>{let e=t.target instanceof Element?t.target.closest("[data-cd-checkout]"):null;if(!e||t.defaultPrevented||t.button||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)return;let i=e.getAttribute("data-cd-checkout")||e.href,s=e.getAttribute("data-cd-success");if(!E(i)||s&&!C(s,""))return;t.preventDefault();let o=I({url:i,successUrl:s});o!==z&&(z=o,o.then(u=>void e.dispatchEvent(new CustomEvent("cd:checkout",{detail:u,bubbles:!0})),()=>{}))}))}var it={version:S,signIn:b,handleCallback:_,exchangeCode:A,checkout:I};window.ChainDaddy=Object.assign(window.ChainDaddy??{},it);L();P({origin:O(document.currentScript?.src)});j();})();
package/dist/cd.js.sri CHANGED
@@ -1 +1 @@
1
- sha384-EF2XlOLUJ0J2U3uy/hlkmXdqyRP0WVm2LYjWdTseg/+QC4El1HD+qMhMlandYcSF
1
+ sha384-fJEKHZn2Glw6r/O/phO2IcSENkiPJsnY5wmjK3psuPFMg86hzPzk7/Mvq07tNiGP
package/dist/embed.js CHANGED
@@ -2,6 +2,8 @@
2
2
  var PROD_ORIGIN = "https://embed.chaindaddy.io";
3
3
  var STAGING_ORIGIN = "https://embed-staging.chaindaddy.io";
4
4
  var READY_TIMEOUT_MS = 8e3;
5
+ var NEAR = "1500px 0px";
6
+ var DOCS = "docs.chaindaddy.io/token/apps-on-your-site#if-the-link-stays";
5
7
  var MAX_HEIGHT = 4e3;
6
8
  var THEME_TOKENS = ["--cd-accent", "--cd-surface", "--cd-text", "--cd-text-muted", "--cd-border"];
7
9
  function embedOriginFor(scriptSrc) {
@@ -48,6 +50,7 @@ function defineEmbedElements(options = {}) {
48
50
  try {
49
51
  window.removeEventListener("message", this.onMessage);
50
52
  clearTimeout(this.timer);
53
+ this.watch?.disconnect();
51
54
  } catch {
52
55
  }
53
56
  }
@@ -84,6 +87,7 @@ function defineEmbedElements(options = {}) {
84
87
  const root = this.shadowRoot;
85
88
  window.removeEventListener("message", this.onMessage);
86
89
  clearTimeout(this.timer);
90
+ this.watch?.disconnect();
87
91
  this.removeAttribute("data-cd-ready");
88
92
  this.removeAttribute("data-cd-unavailable");
89
93
  const src = this.src();
@@ -94,17 +98,43 @@ function defineEmbedElements(options = {}) {
94
98
  }
95
99
  root.innerHTML = `<style>${STYLE}</style><slot></slot>`;
96
100
  const frame = document.createElement("iframe");
97
- frame.setAttribute("loading", "lazy");
98
101
  frame.setAttribute("referrerpolicy", "strict-origin-when-cross-origin");
99
102
  frame.setAttribute("allow", "clipboard-write; fullscreen");
100
103
  frame.setAttribute("title", this.kind === "coin" ? "Coin on Chain Daddy" : "App on Chain Daddy");
101
- frame.src = src;
102
104
  root.appendChild(frame);
103
105
  this.frame = frame;
104
106
  window.addEventListener("message", this.onMessage);
105
- this.timer = setTimeout(() => {
106
- if (!this.hasAttribute("data-cd-ready")) this.unavailable();
107
- }, READY_TIMEOUT_MS);
107
+ const load = () => {
108
+ frame.src = src;
109
+ this.timer = setTimeout(() => {
110
+ if (!this.hasAttribute("data-cd-ready")) this.unavailable();
111
+ }, READY_TIMEOUT_MS);
112
+ };
113
+ if (typeof IntersectionObserver === "undefined") return load();
114
+ this.watch = new IntersectionObserver(
115
+ (entries) => {
116
+ if (!entries.some((e) => e.isIntersecting)) return;
117
+ this.watch?.disconnect();
118
+ load();
119
+ },
120
+ { rootMargin: NEAR }
121
+ );
122
+ this.watch.observe(this);
123
+ }
124
+ /** Says in the console why the link stayed: the page sees only that it did. */
125
+ why() {
126
+ try {
127
+ const q = new URL(this.frame.src).searchParams;
128
+ const app = q.get("app");
129
+ q.delete("mode");
130
+ q.delete("v");
131
+ fetch(`https://${origin === STAGING_ORIGIN ? "staging-api" : "api"}.chaindaddy.io/api/v2/embed/status?${q}`, { credentials: "omit" }).then((r) => r.json()).then((s) => {
132
+ const reason = !s.embeddable ? s.reason : s.interactive ? "not a verified site of the coin" : "not https, or the app did not start";
133
+ console.info(`[Chain Daddy] cd-${this.kind} ${app ?? ""} cannot show here: ${reason}. ${DOCS}`);
134
+ }).catch(() => {
135
+ });
136
+ } catch {
137
+ }
108
138
  }
109
139
  receive(event) {
110
140
  try {
@@ -143,6 +173,7 @@ function defineEmbedElements(options = {}) {
143
173
  if (this.hasAttribute("data-cd-unavailable")) return;
144
174
  this.setAttribute("data-cd-unavailable", "");
145
175
  this.dispatchEvent(new CustomEvent("cd:unavailable"));
176
+ this.why();
146
177
  } catch {
147
178
  }
148
179
  }
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/core.ts
2
- var VERSION = true ? "0.3.1" : "0.0.0";
2
+ var VERSION = true ? "0.3.2" : "0.0.0";
3
3
  function b64url(bytes) {
4
4
  let s = "";
5
5
  for (const b of bytes) s += String.fromCharCode(b);
@@ -217,6 +217,8 @@ function defineSignInButton() {
217
217
  var PROD_ORIGIN = "https://embed.chaindaddy.io";
218
218
  var STAGING_ORIGIN = "https://embed-staging.chaindaddy.io";
219
219
  var READY_TIMEOUT_MS = 8e3;
220
+ var NEAR = "1500px 0px";
221
+ var DOCS = "docs.chaindaddy.io/token/apps-on-your-site#if-the-link-stays";
220
222
  var MAX_HEIGHT = 4e3;
221
223
  var THEME_TOKENS = ["--cd-accent", "--cd-surface", "--cd-text", "--cd-text-muted", "--cd-border"];
222
224
  function embedOriginFor(scriptSrc) {
@@ -263,6 +265,7 @@ function defineEmbedElements(options = {}) {
263
265
  try {
264
266
  window.removeEventListener("message", this.onMessage);
265
267
  clearTimeout(this.timer);
268
+ this.watch?.disconnect();
266
269
  } catch {
267
270
  }
268
271
  }
@@ -299,6 +302,7 @@ function defineEmbedElements(options = {}) {
299
302
  const root = this.shadowRoot;
300
303
  window.removeEventListener("message", this.onMessage);
301
304
  clearTimeout(this.timer);
305
+ this.watch?.disconnect();
302
306
  this.removeAttribute("data-cd-ready");
303
307
  this.removeAttribute("data-cd-unavailable");
304
308
  const src = this.src();
@@ -309,17 +313,43 @@ function defineEmbedElements(options = {}) {
309
313
  }
310
314
  root.innerHTML = `<style>${STYLE2}</style><slot></slot>`;
311
315
  const frame = document.createElement("iframe");
312
- frame.setAttribute("loading", "lazy");
313
316
  frame.setAttribute("referrerpolicy", "strict-origin-when-cross-origin");
314
317
  frame.setAttribute("allow", "clipboard-write; fullscreen");
315
318
  frame.setAttribute("title", this.kind === "coin" ? "Coin on Chain Daddy" : "App on Chain Daddy");
316
- frame.src = src;
317
319
  root.appendChild(frame);
318
320
  this.frame = frame;
319
321
  window.addEventListener("message", this.onMessage);
320
- this.timer = setTimeout(() => {
321
- if (!this.hasAttribute("data-cd-ready")) this.unavailable();
322
- }, READY_TIMEOUT_MS);
322
+ const load = () => {
323
+ frame.src = src;
324
+ this.timer = setTimeout(() => {
325
+ if (!this.hasAttribute("data-cd-ready")) this.unavailable();
326
+ }, READY_TIMEOUT_MS);
327
+ };
328
+ if (typeof IntersectionObserver === "undefined") return load();
329
+ this.watch = new IntersectionObserver(
330
+ (entries) => {
331
+ if (!entries.some((e) => e.isIntersecting)) return;
332
+ this.watch?.disconnect();
333
+ load();
334
+ },
335
+ { rootMargin: NEAR }
336
+ );
337
+ this.watch.observe(this);
338
+ }
339
+ /** Says in the console why the link stayed: the page sees only that it did. */
340
+ why() {
341
+ try {
342
+ const q = new URL(this.frame.src).searchParams;
343
+ const app = q.get("app");
344
+ q.delete("mode");
345
+ q.delete("v");
346
+ fetch(`https://${origin === STAGING_ORIGIN ? "staging-api" : "api"}.chaindaddy.io/api/v2/embed/status?${q}`, { credentials: "omit" }).then((r) => r.json()).then((s) => {
347
+ const reason = !s.embeddable ? s.reason : s.interactive ? "not a verified site of the coin" : "not https, or the app did not start";
348
+ console.info(`[Chain Daddy] cd-${this.kind} ${app ?? ""} cannot show here: ${reason}. ${DOCS}`);
349
+ }).catch(() => {
350
+ });
351
+ } catch {
352
+ }
323
353
  }
324
354
  receive(event) {
325
355
  try {
@@ -358,6 +388,7 @@ function defineEmbedElements(options = {}) {
358
388
  if (this.hasAttribute("data-cd-unavailable")) return;
359
389
  this.setAttribute("data-cd-unavailable", "");
360
390
  this.dispatchEvent(new CustomEvent("cd:unavailable"));
391
+ this.why();
361
392
  } catch {
362
393
  }
363
394
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chaindaddy/js",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Chain Daddy for any website: Sign in with Chain Daddy (a popup and a button), a coin's apps on its own site (<cd-app>), checkout for a coin's store in a popup, and a server-side check that a store order was paid on chain",
5
5
  "homepage": "https://docs.chaindaddy.io/developer/javascript-sdk",
6
6
  "repository": {