@chaindaddy/js 0.3.1 → 0.3.3
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 +516 -40
- package/dist/cd.js +4 -4
- package/dist/cd.js.sri +1 -1
- package/dist/embed.js +36 -5
- package/dist/index.js +82 -20
- package/dist/signin.d.ts +4 -3
- package/dist/signin.js +31 -3
- package/package.json +1 -1
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
|
|
42
|
-
* page itself no longer shows (not reviewed yet, suspended or removed)
|
|
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
|
|
718
|
-
*
|
|
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
|
-
/**
|
|
1891
|
-
|
|
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 `
|
|
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
|
|
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
|
-
*
|
|
2567
|
-
* caller does not manage this token page, or the store key
|
|
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
|
|
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
|
|
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
|
|
3583
|
-
*
|
|
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"
|
|
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
|
|
3623
|
-
*
|
|
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"
|
|
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
|
|
3946
|
-
*
|
|
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"
|
|
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
|
|
4503
|
-
*
|
|
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"
|
|
4858
|
+
"Idempotency-Key": components["parameters"]["IdempotencyKeyRequired"];
|
|
4506
4859
|
};
|
|
4507
4860
|
path?: never;
|
|
4508
4861
|
cookie?: never;
|
|
@@ -4546,9 +4899,24 @@ export interface operations {
|
|
|
4546
4899
|
};
|
|
4547
4900
|
};
|
|
4548
4901
|
400: components["responses"]["IapBadRequest"];
|
|
4902
|
+
/**
|
|
4903
|
+
* @description `AUTHENTICATION_REQUIRED`: no credential at all. The buyer signs in, or your server sends a store key
|
|
4904
|
+
* (`Authorization: Bearer cd_iap_…`); an order for a checkout session is the signed-in buyer's only.
|
|
4905
|
+
* `INVALID_STORE_KEY`: the key is unknown or revoked
|
|
4906
|
+
*/
|
|
4907
|
+
401: {
|
|
4908
|
+
headers: {
|
|
4909
|
+
[name: string]: unknown;
|
|
4910
|
+
};
|
|
4911
|
+
content: {
|
|
4912
|
+
"application/json": components["schemas"]["IapError"];
|
|
4913
|
+
};
|
|
4914
|
+
};
|
|
4549
4915
|
/**
|
|
4550
4916
|
* @description Not allowed, the purchase rule is not met, or the store is not taking orders. `APP_ITEM_NOT_YOURS`: an
|
|
4551
|
-
* app asked for another app's item, or an app's item was asked for outside its app
|
|
4917
|
+
* app asked for another app's item, or an app's item was asked for outside its app. For a checkout
|
|
4918
|
+
* session: `SESSION_REQUIRED` (a store key tried to pay it) or `CHECKOUT_SESSION_WALLET` (the session
|
|
4919
|
+
* names another wallet)
|
|
4552
4920
|
*/
|
|
4553
4921
|
403: {
|
|
4554
4922
|
headers: {
|
|
@@ -4558,12 +4926,26 @@ export interface operations {
|
|
|
4558
4926
|
"application/json": components["schemas"]["IapError"];
|
|
4559
4927
|
};
|
|
4560
4928
|
};
|
|
4929
|
+
/**
|
|
4930
|
+
* @description `NOT_FOUND`: no such store, or no such checkout session. `SKU_NOT_FOR_SALE`: no such item in the store.
|
|
4931
|
+
* `CHECKOUT_SESSIONS_DISABLED`: the order names a checkout session where sessions are not switched on
|
|
4932
|
+
*/
|
|
4933
|
+
404: {
|
|
4934
|
+
headers: {
|
|
4935
|
+
[name: string]: unknown;
|
|
4936
|
+
};
|
|
4937
|
+
content: {
|
|
4938
|
+
"application/json": components["schemas"]["IapError"];
|
|
4939
|
+
};
|
|
4940
|
+
};
|
|
4561
4941
|
/**
|
|
4562
4942
|
* @description Sold out, or the wallet has reached the SKU's per-wallet limit. For an app's item: `APP_TERMS_CHANGED`
|
|
4563
4943
|
* (its developer changed its terms and the page's owner has not accepted them yet), `APP_NOT_INSTALLED`,
|
|
4564
4944
|
* `APP_NOT_SELLING`, `APP_ORDER_LIMIT` (above the per-purchase limit) or `APP_DAILY_LIMIT` (the wallet
|
|
4565
4945
|
* reached what it may pay that app in 24 hours). For an app's item paid through a checkout session:
|
|
4566
|
-
* `APP_ITEM_NOT_SOLD_HERE`, with a `reason`
|
|
4946
|
+
* `APP_ITEM_NOT_SOLD_HERE`, with a `reason`. For any checkout session: `CHECKOUT_SESSION_EXPIRED`,
|
|
4947
|
+
* `CHECKOUT_SESSION_COMPLETE` (already paid) or `CHECKOUT_SESSION_IN_USE` (another wallet is paying it
|
|
4948
|
+
* right now)
|
|
4567
4949
|
*/
|
|
4568
4950
|
409: {
|
|
4569
4951
|
headers: {
|
|
@@ -4635,15 +5017,79 @@ export interface operations {
|
|
|
4635
5017
|
404: components["responses"]["IapNotFound"];
|
|
4636
5018
|
};
|
|
4637
5019
|
};
|
|
5020
|
+
testPayIapOrder: {
|
|
5021
|
+
parameters: {
|
|
5022
|
+
query?: never;
|
|
5023
|
+
header?: never;
|
|
5024
|
+
path: {
|
|
5025
|
+
/** @description `0x` followed by 64 hex characters */
|
|
5026
|
+
orderId: components["parameters"]["IapOrderId"];
|
|
5027
|
+
};
|
|
5028
|
+
cookie?: never;
|
|
5029
|
+
};
|
|
5030
|
+
requestBody?: never;
|
|
5031
|
+
responses: {
|
|
5032
|
+
/** @description The order, confirmed, with its receipt */
|
|
5033
|
+
200: {
|
|
5034
|
+
headers: {
|
|
5035
|
+
[name: string]: unknown;
|
|
5036
|
+
};
|
|
5037
|
+
content: {
|
|
5038
|
+
"application/json": components["schemas"]["IapOrder"];
|
|
5039
|
+
};
|
|
5040
|
+
};
|
|
5041
|
+
/** @description No credential, or a store key that is unknown or revoked (`INVALID_STORE_KEY`) */
|
|
5042
|
+
401: {
|
|
5043
|
+
headers: {
|
|
5044
|
+
[name: string]: unknown;
|
|
5045
|
+
};
|
|
5046
|
+
content: {
|
|
5047
|
+
"application/json": components["schemas"]["IapError"];
|
|
5048
|
+
};
|
|
5049
|
+
};
|
|
5050
|
+
403: components["responses"]["IapForbidden"];
|
|
5051
|
+
/**
|
|
5052
|
+
* @description `NOT_FOUND`: no such order, or it is not an order of the caller's store or coin page.
|
|
5053
|
+
* `TEST_MODE_DISABLED`: test stores are not available here (`testMode` in `GET /api/v2/iap/config`).
|
|
5054
|
+
*/
|
|
5055
|
+
404: {
|
|
5056
|
+
headers: {
|
|
5057
|
+
[name: string]: unknown;
|
|
5058
|
+
};
|
|
5059
|
+
content: {
|
|
5060
|
+
"application/json": components["schemas"]["IapError"];
|
|
5061
|
+
};
|
|
5062
|
+
};
|
|
5063
|
+
/**
|
|
5064
|
+
* @description `LIVE_ORDER`: the order belongs to a live store. `ORDER_EXPIRED`: the order's deadline has passed;
|
|
5065
|
+
* create a new one.
|
|
5066
|
+
*/
|
|
5067
|
+
409: {
|
|
5068
|
+
headers: {
|
|
5069
|
+
[name: string]: unknown;
|
|
5070
|
+
};
|
|
5071
|
+
content: {
|
|
5072
|
+
/**
|
|
5073
|
+
* @example {
|
|
5074
|
+
* "error": "this is a live order: it is paid on chain, and test-pay pays test orders only",
|
|
5075
|
+
* "code": "LIVE_ORDER"
|
|
5076
|
+
* }
|
|
5077
|
+
*/
|
|
5078
|
+
"application/json": components["schemas"]["IapError"];
|
|
5079
|
+
};
|
|
5080
|
+
};
|
|
5081
|
+
};
|
|
5082
|
+
};
|
|
4638
5083
|
createIapCheckoutSession: {
|
|
4639
5084
|
parameters: {
|
|
4640
5085
|
query?: never;
|
|
4641
|
-
header
|
|
5086
|
+
header: {
|
|
4642
5087
|
/**
|
|
4643
|
-
* @description
|
|
4644
|
-
*
|
|
5088
|
+
* @description Required: a call without it answers 400 `IDEMPOTENCY_KEY_REQUIRED`. Send one value per attempt (a UUID,
|
|
5089
|
+
* or an id of your own such as `cart-8842-attempt-1`), up to 255 characters. Retrying with the same key
|
|
5090
|
+
* and the same request returns the first result and never sells, grants or consumes twice.
|
|
4645
5091
|
*/
|
|
4646
|
-
"Idempotency-Key"
|
|
5092
|
+
"Idempotency-Key": components["parameters"]["IdempotencyKeyRequired"];
|
|
4647
5093
|
};
|
|
4648
5094
|
path?: never;
|
|
4649
5095
|
cookie?: never;
|
|
@@ -4719,7 +5165,11 @@ export interface operations {
|
|
|
4719
5165
|
"application/json": components["schemas"]["IapError"];
|
|
4720
5166
|
};
|
|
4721
5167
|
};
|
|
4722
|
-
/**
|
|
5168
|
+
/**
|
|
5169
|
+
* @description `AUTHENTICATION_REQUIRED`: no credential at all; send the store key as
|
|
5170
|
+
* `Authorization: Bearer cd_iap_…`. `INVALID_STORE_KEY`: the key is unknown or revoked.
|
|
5171
|
+
* `TEST_MODE_DISABLED`: a test key where test mode is not available
|
|
5172
|
+
*/
|
|
4723
5173
|
401: {
|
|
4724
5174
|
headers: {
|
|
4725
5175
|
[name: string]: unknown;
|
|
@@ -4800,7 +5250,11 @@ export interface operations {
|
|
|
4800
5250
|
"application/json": components["schemas"]["IapCheckoutSession"];
|
|
4801
5251
|
};
|
|
4802
5252
|
};
|
|
4803
|
-
/**
|
|
5253
|
+
/**
|
|
5254
|
+
* @description `AUTHENTICATION_REQUIRED`: no credential at all; send the store key as
|
|
5255
|
+
* `Authorization: Bearer cd_iap_…`. `INVALID_STORE_KEY`: the key is unknown or revoked.
|
|
5256
|
+
* `TEST_MODE_DISABLED`: a test key where test mode is not available
|
|
5257
|
+
*/
|
|
4804
5258
|
401: {
|
|
4805
5259
|
headers: {
|
|
4806
5260
|
[name: string]: unknown;
|
|
@@ -4818,7 +5272,18 @@ export interface operations {
|
|
|
4818
5272
|
"application/json": components["schemas"]["IapError"];
|
|
4819
5273
|
};
|
|
4820
5274
|
};
|
|
4821
|
-
|
|
5275
|
+
/**
|
|
5276
|
+
* @description `NOT_FOUND`: no such session, or it belongs to another store. `CHECKOUT_SESSIONS_DISABLED`: checkout
|
|
5277
|
+
* sessions are not switched on here, or are open to test stores only and the key is a live store's
|
|
5278
|
+
*/
|
|
5279
|
+
404: {
|
|
5280
|
+
headers: {
|
|
5281
|
+
[name: string]: unknown;
|
|
5282
|
+
};
|
|
5283
|
+
content: {
|
|
5284
|
+
"application/json": components["schemas"]["IapError"];
|
|
5285
|
+
};
|
|
5286
|
+
};
|
|
4822
5287
|
};
|
|
4823
5288
|
};
|
|
4824
5289
|
getIapPublicCheckoutSession: {
|
|
@@ -4845,7 +5310,18 @@ export interface operations {
|
|
|
4845
5310
|
"application/json": components["schemas"]["IapPublicCheckoutSession"];
|
|
4846
5311
|
};
|
|
4847
5312
|
};
|
|
4848
|
-
|
|
5313
|
+
/**
|
|
5314
|
+
* @description `NOT_FOUND`: no such session. `CHECKOUT_SESSIONS_DISABLED`: checkout sessions are not switched on
|
|
5315
|
+
* here, or are open to test stores only and the session is a live store's
|
|
5316
|
+
*/
|
|
5317
|
+
404: {
|
|
5318
|
+
headers: {
|
|
5319
|
+
[name: string]: unknown;
|
|
5320
|
+
};
|
|
5321
|
+
content: {
|
|
5322
|
+
"application/json": components["schemas"]["IapError"];
|
|
5323
|
+
};
|
|
5324
|
+
};
|
|
4849
5325
|
};
|
|
4850
5326
|
};
|
|
4851
5327
|
listWebhooks: {
|
package/dist/cd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";(()=>{var
|
|
1
|
+
"use strict";(()=>{var R="0.3.3";function C(t){let e="";for(let i of t)e+=String.fromCharCode(i);return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function w(){return C(crypto.getRandomValues(new Uint8Array(32)))}function T(t,e,i,o,r){let u=n=>{if(n&&typeof n=="object"&&n.type===t)try{i(n)}catch{}},l=n=>{n.origin===e&&(!r||n.source===r)&&u(n.data)};window.addEventListener("message",l);let a=null;if(o&&typeof BroadcastChannel=="function"&&e===location.origin)try{a=new BroadcastChannel(o),a.onmessage=n=>u(n.data)}catch{a=null}return()=>{window.removeEventListener("message",l),a?.close()}}var y={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 F="https://api.chaindaddy.io",G={"https://api.chaindaddy.io":"https://chaindaddy.io/_oauth/authorize","https://staging-api.chaindaddy.io":"https://staging.chaindaddy.io/_oauth/authorize"},b="cd-signin",U="cd:signin",v="cd.signin",_=300;async function K(t){let e=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(t));return C(new Uint8Array(e))}function W(t="openid"){let e=t.split(/\s+/).filter(Boolean);return(e.includes("openid")?e:["openid",...e]).join(" ")}async function Y(t,e,i){let o=new URLSearchParams({response_type:"code",client_id:e.clientId,redirect_uri:e.redirectUri,scope:W(t.scope),state:e.state,nonce:e.nonce,code_challenge:await K(e.verifier),code_challenge_method:"S256"});return t.claims&&o.set("claims",typeof t.claims=="string"?t.claims:JSON.stringify(t.claims)),`${i}?${o}`}function A(t,e){return{code:e,codeVerifier:t.verifier,state:t.state,nonce:t.nonce,issuer:t.issuer,clientId:t.clientId,redirectUri:t.redirectUri}}var m;function k(t){let e=(t.issuer??F).replace(/\/$/,""),i=t.authorizeUrl??G[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"));if(m&&!m.popup.closed)return m.popup.focus(),m.waiting;let o={state:w(),nonce:w(),verifier:w(),issuer:e,clientId:t.clientId,redirectUri:t.redirectUri};y.set(v,JSON.stringify(o));let r=window.open("",b,"popup,width=480,height=720"),u=(async()=>{let l=await Y(t,o,i);if(!r)return location.assign(l),null;r.location.href=l;let a=new URL(t.redirectUri,location.href).origin;return new Promise((n,c)=>{let s,d=()=>{g(),clearInterval(h),clearTimeout(s)},g=T(U,a,p=>{p.state===o.state&&(d(),y.remove(v),typeof p.error=="string"&&p.error?c(new Error(p.error)):p.iss!==e?c(new Error("the answer came from another issuer")):typeof p.code!="string"||!p.code?c(new Error("the answer carried no code")):n(A(o,p.code)))},b,r),h=setInterval(()=>{r.closed&&(clearInterval(h),s=setTimeout(()=>{d(),c(new Error("closed"))},_))},_);t.signal?.addEventListener("abort",()=>{d(),c(new Error("aborted"))})})})();if(r){m={popup:r,waiting:u};let l=()=>{m&&m.popup===r&&(m=void 0)};u.then(l,l)}return u}async function L(){let t=new URLSearchParams(location.search),e={type:U,code:t.get("code"),state:t.get("state"),iss:t.get("iss"),error:t.get("error")};if(window.name===b){try{window.opener?.postMessage(e,location.origin)}catch{}try{let r=new BroadcastChannel(b);r.postMessage(e),r.close()}catch{}return window.close(),null}let i=y.get(v);y.remove(v);let o=i?JSON.parse(i):null;if(!o||e.state!==o.state)throw new Error("the sign-in state does not match: start again");if(e.error)throw new Error(e.error);if(e.iss!==o.issuer)throw new Error("the answer came from another issuer");if(!e.code)throw new Error("the answer carried no code");return A(o,e.code)}async function M(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 J='<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>',X=`
|
|
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
|
|
10
|
+
`;function P(){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",o=this.getAttribute("theme")==="dark"?"dark":"light";this.root.innerHTML=`<style>${X}</style><button type="button" part="button" data-theme="${o}">${J}<span></span></button>`;let r=this.root.querySelector("button");r.querySelector("span").textContent=i,r.addEventListener("click",()=>this.start())}start(){let i=r=>this.getAttribute(r)||void 0,o=k({clientId:i("client-id")??"",redirectUri:i("redirect-uri")??"",scope:i("scope"),claims:i("claims"),issuer:i("issuer"),authorizeUrl:i("authorize-url")});o!==this.reporting&&(this.reporting=o,o.then(r=>r&&this.dispatchEvent(new CustomEvent("cd-signin",{detail:r,bubbles:!0,composed:!0})),r=>this.dispatchEvent(new CustomEvent("cd-signin-error",{detail:{message:String(r?.message??r)},bubbles:!0,composed:!0}))))}}t.observedAttributes=["theme","text"],customElements.define("cd-signin-button",t)}var O="https://embed.chaindaddy.io",E="https://embed-staging.chaindaddy.io";var Q="1500px 0px",Z="docs.chaindaddy.io/token/apps-on-your-site#if-the-link-stays";var tt=["--cd-accent","--cd-surface","--cd-text","--cd-text-muted","--cd-border"];function $(t){try{if(t&&new URL(t).hostname==="js.staging.chaindaddy.io")return E}catch{}return O}var et=`
|
|
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
|
-
`,
|
|
16
|
+
`,nt=`
|
|
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
|
|
22
|
+
`;function D(t={}){try{if(typeof customElements>"u"||typeof HTMLElement>"u")return;let e=t.origin===E?E:O;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(),s=(this.getAttribute("app")??"").trim();if(!/^[0-9]{1,18}$/.test(n)||!c||this.kind==="app"&&!s)return null;let d=new URLSearchParams({crown:n,chain:c});return this.kind==="app"?d.set("app",s):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>${et}</style><slot></slot>`;let s=document.createElement("iframe");s.setAttribute("referrerpolicy","strict-origin-when-cross-origin"),s.setAttribute("allow","clipboard-write; fullscreen"),s.setAttribute("title",this.kind==="coin"?"Coin on Chain Daddy":"App on Chain Daddy"),n.appendChild(s),this.frame=s,window.addEventListener("message",this.onMessage);let d=()=>{s.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:Q}),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===E?"staging-api":"api"}.chaindaddy.io/api/v2/embed/status?${n}`,{credentials:"omit"}).then(s=>s.json()).then(s=>{let d=s.embeddable?s.interactive?"not a verified site of the coin":"not https, or the app did not start":s.reason;console.info(`[Chain Daddy] cd-${this.kind} ${c??""} cannot show here: ${d}. ${Z}`)}).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 s=Number(c.height);Number.isFinite(s)&&(this.frame.style.height=`${Math.max(1,Math.min(4e3,Math.ceil(s)))}px`)}else c.type==="cd:unavailable"&&this.unavailable()}catch{}}sendTheme(){let n={},c=getComputedStyle(this);for(let s of tt){let d=c.getPropertyValue(s).trim();d&&(n[s]=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 o extends i{}class r 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>${nt}</style><button type="button" part="button"></button>`;let s=c.querySelector("button");s.textContent=(a.textContent??"").trim()||"Buy on Chain Daddy",s.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",o],["cd-coin",r],["cd-buy",u]])customElements.get(l)||customElements.define(l,a)}catch{}}var it=["https://chaindaddy.io","https://staging.chaindaddy.io"],H=()=>"cd-checkout-"+w(),N="popup,width=480,height=820",B=300,rt=7;function I(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&&it.includes(e.origin)&&!e.username&&!e.password?{href:e.origin+e.pathname,origin:e.origin,id:i[1]}:null}function x(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 o=u=>{let l=u.split("=")[0];try{return decodeURIComponent(l.replace(/\+/g," "))}catch{return l}},r=i.search.slice(1).split("&").filter(u=>u&&o(u)!=="session_id");return r.push("session_id="+encodeURIComponent(e)),i.origin+i.pathname+"?"+r.join("&")+i.hash}var f;function z(t,e,i){return new Promise(o=>{let r=!1,u=0,l,a=d=>{clearInterval(s),clearTimeout(l),window.removeEventListener("message",n),i&&d.status==="completed"&&location.assign(i),o(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"&&(r=!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 p={id:e.id,status:h.status};typeof h.orderId=="string"&&/^[\w-]{1,128}$/.test(h.orderId)&&(p.orderId=h.orderId),a({status:"completed",session:p})}}},c=()=>{try{t.postMessage({type:"cd:checkout.hello"},e.origin)}catch{}},s=setInterval(()=>{t.closed?(clearInterval(s),l=setTimeout(()=>a({status:"closed"}),B)):(!r||++u%rt===0)&&c()},B);window.addEventListener("message",n),c()})}function S(t){let e=()=>new Error("checkout: url is not a Chain Daddy checkout session link"),i=(r,u,l)=>{f={popup:r,result:u,href:l};let a=()=>{f&&f.popup===r&&(f=void 0)};return u.then(a,a),u},o=r=>(location.assign(r.href),{status:"redirected"});try{let{url:r,successUrl:u}=t;if(u&&!x(u,""))throw new Error("checkout: successUrl is not a web address");let l=s=>u?x(u,s.id):null,a=typeof r=="function"?null:I(r);if(typeof r!="function"&&!a)throw e();if(f&&!f.popup.closed&&(!a||f.href===a.href))return f.popup.focus(),f.result;if(a){let s=window.open(a.href,H(),N);return s?i(s,z(s,a,l(a)),a.href):Promise.resolve(o(a))}let n=window.open("",H(),N),c=new Promise(s=>s(r())).then(s=>{let d=I(s);if(!d)throw n?.close(),e();return n?(n.location.href=d.href,z(n,d,l(d))):o(d)},s=>{throw n?.close(),s});return n?i(n,c):c}catch(r){return Promise.reject(r)}}var j=!1,q;function V(){j||typeof document>"u"||(j=!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,o=e.getAttribute("data-cd-success");if(!I(i)||o&&!x(o,""))return;t.preventDefault();let r=S({url:i,successUrl:o});r!==q&&(q=r,r.then(u=>void e.dispatchEvent(new CustomEvent("cd:checkout",{detail:u,bubbles:!0})),()=>{}))}))}var st={version:R,signIn:k,handleCallback:L,exchangeCode:M,checkout:S};window.ChainDaddy=Object.assign(window.ChainDaddy??{},st);P();D({origin:$(document.currentScript?.src)});V();})();
|
package/dist/cd.js.sri
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
sha384-
|
|
1
|
+
sha384-x7XiOLUzANtLnqFmv/hWd2uhGI7c+jlhVivakZ7LYHklw9zgK/diic0+fJom/mXU
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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.
|
|
2
|
+
var VERSION = true ? "0.3.3" : "0.0.0";
|
|
3
3
|
function b64url(bytes) {
|
|
4
4
|
let s = "";
|
|
5
5
|
for (const b of bytes) s += String.fromCharCode(b);
|
|
@@ -66,6 +66,7 @@ var AUTHORIZE = {
|
|
|
66
66
|
var POPUP_NAME = "cd-signin";
|
|
67
67
|
var MESSAGE = "cd:signin";
|
|
68
68
|
var STORAGE_KEY = "cd.signin";
|
|
69
|
+
var TICK_MS = 300;
|
|
69
70
|
async function challengeFor(verifier) {
|
|
70
71
|
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
|
|
71
72
|
return b64url(new Uint8Array(digest));
|
|
@@ -91,15 +92,20 @@ async function authorizeURL(o, p, authorize) {
|
|
|
91
92
|
function result(p, code) {
|
|
92
93
|
return { code, codeVerifier: p.verifier, state: p.state, nonce: p.nonce, issuer: p.issuer, clientId: p.clientId, redirectUri: p.redirectUri };
|
|
93
94
|
}
|
|
95
|
+
var current;
|
|
94
96
|
function signIn(o) {
|
|
95
97
|
const issuer = (o.issuer ?? DEFAULT_ISSUER).replace(/\/$/, "");
|
|
96
98
|
const authorize = o.authorizeUrl ?? AUTHORIZE[issuer];
|
|
97
99
|
if (!authorize) return Promise.reject(new Error("authorizeUrl is required for this issuer"));
|
|
98
100
|
if (!o.clientId || !o.redirectUri) return Promise.reject(new Error("clientId and redirectUri are required"));
|
|
101
|
+
if (current && !current.popup.closed) {
|
|
102
|
+
current.popup.focus();
|
|
103
|
+
return current.waiting;
|
|
104
|
+
}
|
|
99
105
|
const p = { state: randomToken(), nonce: randomToken(), verifier: randomToken(), issuer, clientId: o.clientId, redirectUri: o.redirectUri };
|
|
100
106
|
store.set(STORAGE_KEY, JSON.stringify(p));
|
|
101
107
|
const popup = window.open("", POPUP_NAME, "popup,width=480,height=720");
|
|
102
|
-
|
|
108
|
+
const waiting = (async () => {
|
|
103
109
|
const url = await authorizeURL(o, p, authorize);
|
|
104
110
|
if (!popup) {
|
|
105
111
|
location.assign(url);
|
|
@@ -108,21 +114,43 @@ function signIn(o) {
|
|
|
108
114
|
popup.location.href = url;
|
|
109
115
|
const callbackOrigin = new URL(o.redirectUri, location.href).origin;
|
|
110
116
|
return new Promise((resolve, reject) => {
|
|
117
|
+
let last;
|
|
118
|
+
const end = () => {
|
|
119
|
+
stop();
|
|
120
|
+
clearInterval(timer);
|
|
121
|
+
clearTimeout(last);
|
|
122
|
+
};
|
|
111
123
|
const stop = listen(MESSAGE, callbackOrigin, (m) => {
|
|
112
124
|
if (m.state !== p.state) return;
|
|
113
|
-
|
|
125
|
+
end();
|
|
114
126
|
store.remove(STORAGE_KEY);
|
|
115
127
|
if (typeof m.error === "string" && m.error) reject(new Error(m.error));
|
|
116
128
|
else if (m.iss !== issuer) reject(new Error("the answer came from another issuer"));
|
|
117
129
|
else if (typeof m.code !== "string" || !m.code) reject(new Error("the answer carried no code"));
|
|
118
130
|
else resolve(result(p, m.code));
|
|
119
131
|
}, POPUP_NAME, popup);
|
|
132
|
+
const timer = setInterval(() => {
|
|
133
|
+
if (!popup.closed) return;
|
|
134
|
+
clearInterval(timer);
|
|
135
|
+
last = setTimeout(() => {
|
|
136
|
+
end();
|
|
137
|
+
reject(new Error("closed"));
|
|
138
|
+
}, TICK_MS);
|
|
139
|
+
}, TICK_MS);
|
|
120
140
|
o.signal?.addEventListener("abort", () => {
|
|
121
|
-
|
|
141
|
+
end();
|
|
122
142
|
reject(new Error("aborted"));
|
|
123
143
|
});
|
|
124
144
|
});
|
|
125
145
|
})();
|
|
146
|
+
if (popup) {
|
|
147
|
+
current = { popup, waiting };
|
|
148
|
+
const over = () => {
|
|
149
|
+
if (current && current.popup === popup) current = void 0;
|
|
150
|
+
};
|
|
151
|
+
waiting.then(over, over);
|
|
152
|
+
}
|
|
153
|
+
return waiting;
|
|
126
154
|
}
|
|
127
155
|
async function handleCallback() {
|
|
128
156
|
const q = new URLSearchParams(location.search);
|
|
@@ -196,14 +224,17 @@ function defineSignInButton() {
|
|
|
196
224
|
}
|
|
197
225
|
start() {
|
|
198
226
|
const attr = (n) => this.getAttribute(n) || void 0;
|
|
199
|
-
signIn({
|
|
227
|
+
const visit = signIn({
|
|
200
228
|
clientId: attr("client-id") ?? "",
|
|
201
229
|
redirectUri: attr("redirect-uri") ?? "",
|
|
202
230
|
scope: attr("scope"),
|
|
203
231
|
claims: attr("claims"),
|
|
204
232
|
issuer: attr("issuer"),
|
|
205
233
|
authorizeUrl: attr("authorize-url")
|
|
206
|
-
})
|
|
234
|
+
});
|
|
235
|
+
if (visit === this.reporting) return;
|
|
236
|
+
this.reporting = visit;
|
|
237
|
+
visit.then(
|
|
207
238
|
(r) => r && this.dispatchEvent(new CustomEvent("cd-signin", { detail: r, bubbles: true, composed: true })),
|
|
208
239
|
(e) => this.dispatchEvent(new CustomEvent("cd-signin-error", { detail: { message: String(e?.message ?? e) }, bubbles: true, composed: true }))
|
|
209
240
|
);
|
|
@@ -217,6 +248,8 @@ function defineSignInButton() {
|
|
|
217
248
|
var PROD_ORIGIN = "https://embed.chaindaddy.io";
|
|
218
249
|
var STAGING_ORIGIN = "https://embed-staging.chaindaddy.io";
|
|
219
250
|
var READY_TIMEOUT_MS = 8e3;
|
|
251
|
+
var NEAR = "1500px 0px";
|
|
252
|
+
var DOCS = "docs.chaindaddy.io/token/apps-on-your-site#if-the-link-stays";
|
|
220
253
|
var MAX_HEIGHT = 4e3;
|
|
221
254
|
var THEME_TOKENS = ["--cd-accent", "--cd-surface", "--cd-text", "--cd-text-muted", "--cd-border"];
|
|
222
255
|
function embedOriginFor(scriptSrc) {
|
|
@@ -263,6 +296,7 @@ function defineEmbedElements(options = {}) {
|
|
|
263
296
|
try {
|
|
264
297
|
window.removeEventListener("message", this.onMessage);
|
|
265
298
|
clearTimeout(this.timer);
|
|
299
|
+
this.watch?.disconnect();
|
|
266
300
|
} catch {
|
|
267
301
|
}
|
|
268
302
|
}
|
|
@@ -299,6 +333,7 @@ function defineEmbedElements(options = {}) {
|
|
|
299
333
|
const root = this.shadowRoot;
|
|
300
334
|
window.removeEventListener("message", this.onMessage);
|
|
301
335
|
clearTimeout(this.timer);
|
|
336
|
+
this.watch?.disconnect();
|
|
302
337
|
this.removeAttribute("data-cd-ready");
|
|
303
338
|
this.removeAttribute("data-cd-unavailable");
|
|
304
339
|
const src = this.src();
|
|
@@ -309,17 +344,43 @@ function defineEmbedElements(options = {}) {
|
|
|
309
344
|
}
|
|
310
345
|
root.innerHTML = `<style>${STYLE2}</style><slot></slot>`;
|
|
311
346
|
const frame = document.createElement("iframe");
|
|
312
|
-
frame.setAttribute("loading", "lazy");
|
|
313
347
|
frame.setAttribute("referrerpolicy", "strict-origin-when-cross-origin");
|
|
314
348
|
frame.setAttribute("allow", "clipboard-write; fullscreen");
|
|
315
349
|
frame.setAttribute("title", this.kind === "coin" ? "Coin on Chain Daddy" : "App on Chain Daddy");
|
|
316
|
-
frame.src = src;
|
|
317
350
|
root.appendChild(frame);
|
|
318
351
|
this.frame = frame;
|
|
319
352
|
window.addEventListener("message", this.onMessage);
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
353
|
+
const load = () => {
|
|
354
|
+
frame.src = src;
|
|
355
|
+
this.timer = setTimeout(() => {
|
|
356
|
+
if (!this.hasAttribute("data-cd-ready")) this.unavailable();
|
|
357
|
+
}, READY_TIMEOUT_MS);
|
|
358
|
+
};
|
|
359
|
+
if (typeof IntersectionObserver === "undefined") return load();
|
|
360
|
+
this.watch = new IntersectionObserver(
|
|
361
|
+
(entries) => {
|
|
362
|
+
if (!entries.some((e) => e.isIntersecting)) return;
|
|
363
|
+
this.watch?.disconnect();
|
|
364
|
+
load();
|
|
365
|
+
},
|
|
366
|
+
{ rootMargin: NEAR }
|
|
367
|
+
);
|
|
368
|
+
this.watch.observe(this);
|
|
369
|
+
}
|
|
370
|
+
/** Says in the console why the link stayed: the page sees only that it did. */
|
|
371
|
+
why() {
|
|
372
|
+
try {
|
|
373
|
+
const q = new URL(this.frame.src).searchParams;
|
|
374
|
+
const app = q.get("app");
|
|
375
|
+
q.delete("mode");
|
|
376
|
+
q.delete("v");
|
|
377
|
+
fetch(`https://${origin === STAGING_ORIGIN ? "staging-api" : "api"}.chaindaddy.io/api/v2/embed/status?${q}`, { credentials: "omit" }).then((r) => r.json()).then((s) => {
|
|
378
|
+
const reason = !s.embeddable ? s.reason : s.interactive ? "not a verified site of the coin" : "not https, or the app did not start";
|
|
379
|
+
console.info(`[Chain Daddy] cd-${this.kind} ${app ?? ""} cannot show here: ${reason}. ${DOCS}`);
|
|
380
|
+
}).catch(() => {
|
|
381
|
+
});
|
|
382
|
+
} catch {
|
|
383
|
+
}
|
|
323
384
|
}
|
|
324
385
|
receive(event) {
|
|
325
386
|
try {
|
|
@@ -358,6 +419,7 @@ function defineEmbedElements(options = {}) {
|
|
|
358
419
|
if (this.hasAttribute("data-cd-unavailable")) return;
|
|
359
420
|
this.setAttribute("data-cd-unavailable", "");
|
|
360
421
|
this.dispatchEvent(new CustomEvent("cd:unavailable"));
|
|
422
|
+
this.why();
|
|
361
423
|
} catch {
|
|
362
424
|
}
|
|
363
425
|
}
|
|
@@ -402,7 +464,7 @@ function defineEmbedElements(options = {}) {
|
|
|
402
464
|
var ORIGINS = ["https://chaindaddy.io", "https://staging.chaindaddy.io"];
|
|
403
465
|
var windowName = () => "cd-checkout-" + randomToken();
|
|
404
466
|
var FEATURES = "popup,width=480,height=820";
|
|
405
|
-
var
|
|
467
|
+
var TICK_MS2 = 300;
|
|
406
468
|
var QUIET_TICKS = 7;
|
|
407
469
|
function parseCheckoutUrl(url) {
|
|
408
470
|
if (typeof url !== "string") return null;
|
|
@@ -436,7 +498,7 @@ function successTarget(successUrl, id) {
|
|
|
436
498
|
pairs.push("session_id=" + encodeURIComponent(id));
|
|
437
499
|
return u.origin + u.pathname + "?" + pairs.join("&") + u.hash;
|
|
438
500
|
}
|
|
439
|
-
var
|
|
501
|
+
var current2;
|
|
440
502
|
function wait(popup, target, success) {
|
|
441
503
|
return new Promise((resolve) => {
|
|
442
504
|
let ready = false;
|
|
@@ -472,9 +534,9 @@ function wait(popup, target, success) {
|
|
|
472
534
|
const timer = setInterval(() => {
|
|
473
535
|
if (popup.closed) {
|
|
474
536
|
clearInterval(timer);
|
|
475
|
-
last = setTimeout(() => done({ status: "closed" }),
|
|
537
|
+
last = setTimeout(() => done({ status: "closed" }), TICK_MS2);
|
|
476
538
|
} else if (!ready || ++quiet % QUIET_TICKS === 0) hello();
|
|
477
|
-
},
|
|
539
|
+
}, TICK_MS2);
|
|
478
540
|
window.addEventListener("message", onMessage);
|
|
479
541
|
hello();
|
|
480
542
|
});
|
|
@@ -482,9 +544,9 @@ function wait(popup, target, success) {
|
|
|
482
544
|
function checkout(o) {
|
|
483
545
|
const refused = () => new Error("checkout: url is not a Chain Daddy checkout session link");
|
|
484
546
|
const track = (popup, result2, href) => {
|
|
485
|
-
|
|
547
|
+
current2 = { popup, result: result2, href };
|
|
486
548
|
const over = () => {
|
|
487
|
-
if (
|
|
549
|
+
if (current2 && current2.popup === popup) current2 = void 0;
|
|
488
550
|
};
|
|
489
551
|
result2.then(over, over);
|
|
490
552
|
return result2;
|
|
@@ -499,9 +561,9 @@ function checkout(o) {
|
|
|
499
561
|
const success = (t) => successUrl ? successTarget(successUrl, t.id) : null;
|
|
500
562
|
const target = typeof url === "function" ? null : parseCheckoutUrl(url);
|
|
501
563
|
if (typeof url !== "function" && !target) throw refused();
|
|
502
|
-
if (
|
|
503
|
-
|
|
504
|
-
return
|
|
564
|
+
if (current2 && !current2.popup.closed && (!target || current2.href === target.href)) {
|
|
565
|
+
current2.popup.focus();
|
|
566
|
+
return current2.result;
|
|
505
567
|
}
|
|
506
568
|
if (target) {
|
|
507
569
|
const popup2 = window.open(target.href, windowName(), FEATURES);
|
package/dist/signin.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface SignInOptions {
|
|
|
12
12
|
issuer?: string;
|
|
13
13
|
/** The consent page; derived from the issuer for Chain Daddy's own. */
|
|
14
14
|
authorizeUrl?: string;
|
|
15
|
-
/** Stops waiting
|
|
15
|
+
/** Stops waiting: the call rejects "aborted". */
|
|
16
16
|
signal?: AbortSignal;
|
|
17
17
|
}
|
|
18
18
|
/** What your server exchanges at the token endpoint (or exchangeCode does here). */
|
|
@@ -32,8 +32,9 @@ export declare function challengeFor(verifier: string): Promise<string>;
|
|
|
32
32
|
* before anything is awaited, or a browser blocks it. Resolves with the code
|
|
33
33
|
* once the visitor approves; resolves null when the popup was blocked and the
|
|
34
34
|
* page is redirecting instead (your callback page then calls handleCallback).
|
|
35
|
-
* Rejects when they cancel (access_denied)
|
|
36
|
-
* request.
|
|
35
|
+
* Rejects when they cancel (access_denied), when they close the popup without
|
|
36
|
+
* answering (closed), or when the answer is not for this request. While one
|
|
37
|
+
* sign-in is open, another call shows its popup and returns the same promise.
|
|
37
38
|
*/
|
|
38
39
|
export declare function signIn(o: SignInOptions): Promise<SignInResult | null>;
|
|
39
40
|
/**
|
package/dist/signin.js
CHANGED
|
@@ -65,6 +65,7 @@ var AUTHORIZE = {
|
|
|
65
65
|
var POPUP_NAME = "cd-signin";
|
|
66
66
|
var MESSAGE = "cd:signin";
|
|
67
67
|
var STORAGE_KEY = "cd.signin";
|
|
68
|
+
var TICK_MS = 300;
|
|
68
69
|
async function challengeFor(verifier) {
|
|
69
70
|
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
|
|
70
71
|
return b64url(new Uint8Array(digest));
|
|
@@ -90,15 +91,20 @@ async function authorizeURL(o, p, authorize) {
|
|
|
90
91
|
function result(p, code) {
|
|
91
92
|
return { code, codeVerifier: p.verifier, state: p.state, nonce: p.nonce, issuer: p.issuer, clientId: p.clientId, redirectUri: p.redirectUri };
|
|
92
93
|
}
|
|
94
|
+
var current;
|
|
93
95
|
function signIn(o) {
|
|
94
96
|
const issuer = (o.issuer ?? DEFAULT_ISSUER).replace(/\/$/, "");
|
|
95
97
|
const authorize = o.authorizeUrl ?? AUTHORIZE[issuer];
|
|
96
98
|
if (!authorize) return Promise.reject(new Error("authorizeUrl is required for this issuer"));
|
|
97
99
|
if (!o.clientId || !o.redirectUri) return Promise.reject(new Error("clientId and redirectUri are required"));
|
|
100
|
+
if (current && !current.popup.closed) {
|
|
101
|
+
current.popup.focus();
|
|
102
|
+
return current.waiting;
|
|
103
|
+
}
|
|
98
104
|
const p = { state: randomToken(), nonce: randomToken(), verifier: randomToken(), issuer, clientId: o.clientId, redirectUri: o.redirectUri };
|
|
99
105
|
store.set(STORAGE_KEY, JSON.stringify(p));
|
|
100
106
|
const popup = window.open("", POPUP_NAME, "popup,width=480,height=720");
|
|
101
|
-
|
|
107
|
+
const waiting = (async () => {
|
|
102
108
|
const url = await authorizeURL(o, p, authorize);
|
|
103
109
|
if (!popup) {
|
|
104
110
|
location.assign(url);
|
|
@@ -107,21 +113,43 @@ function signIn(o) {
|
|
|
107
113
|
popup.location.href = url;
|
|
108
114
|
const callbackOrigin = new URL(o.redirectUri, location.href).origin;
|
|
109
115
|
return new Promise((resolve, reject) => {
|
|
116
|
+
let last;
|
|
117
|
+
const end = () => {
|
|
118
|
+
stop();
|
|
119
|
+
clearInterval(timer);
|
|
120
|
+
clearTimeout(last);
|
|
121
|
+
};
|
|
110
122
|
const stop = listen(MESSAGE, callbackOrigin, (m) => {
|
|
111
123
|
if (m.state !== p.state) return;
|
|
112
|
-
|
|
124
|
+
end();
|
|
113
125
|
store.remove(STORAGE_KEY);
|
|
114
126
|
if (typeof m.error === "string" && m.error) reject(new Error(m.error));
|
|
115
127
|
else if (m.iss !== issuer) reject(new Error("the answer came from another issuer"));
|
|
116
128
|
else if (typeof m.code !== "string" || !m.code) reject(new Error("the answer carried no code"));
|
|
117
129
|
else resolve(result(p, m.code));
|
|
118
130
|
}, POPUP_NAME, popup);
|
|
131
|
+
const timer = setInterval(() => {
|
|
132
|
+
if (!popup.closed) return;
|
|
133
|
+
clearInterval(timer);
|
|
134
|
+
last = setTimeout(() => {
|
|
135
|
+
end();
|
|
136
|
+
reject(new Error("closed"));
|
|
137
|
+
}, TICK_MS);
|
|
138
|
+
}, TICK_MS);
|
|
119
139
|
o.signal?.addEventListener("abort", () => {
|
|
120
|
-
|
|
140
|
+
end();
|
|
121
141
|
reject(new Error("aborted"));
|
|
122
142
|
});
|
|
123
143
|
});
|
|
124
144
|
})();
|
|
145
|
+
if (popup) {
|
|
146
|
+
current = { popup, waiting };
|
|
147
|
+
const over = () => {
|
|
148
|
+
if (current && current.popup === popup) current = void 0;
|
|
149
|
+
};
|
|
150
|
+
waiting.then(over, over);
|
|
151
|
+
}
|
|
152
|
+
return waiting;
|
|
125
153
|
}
|
|
126
154
|
async function handleCallback() {
|
|
127
155
|
const q = new URLSearchParams(location.search);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chaindaddy/js",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
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": {
|