@phala/pay 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -31
- package/dist/checkout.d.ts +12 -4
- package/dist/checkout.d.ts.map +1 -1
- package/dist/checkout.js +64 -15
- package/dist/checkout.js.map +1 -1
- package/dist/client.d.ts +4 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +5 -0
- package/dist/client.js.map +1 -1
- package/dist/deposit-address.d.ts.map +1 -1
- package/dist/deposit-address.js +1 -0
- package/dist/deposit-address.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/quote.d.ts +2 -0
- package/dist/quote.d.ts.map +1 -1
- package/dist/quote.js +3 -0
- package/dist/quote.js.map +1 -1
- package/dist/transactions.d.ts +21 -0
- package/dist/transactions.d.ts.map +1 -0
- package/dist/transactions.js +39 -0
- package/dist/transactions.js.map +1 -0
- package/dist/wallet.d.ts.map +1 -1
- package/dist/wallet.js +4 -1
- package/dist/wallet.js.map +1 -1
- package/package.json +2 -6
- package/src/checkout.ts +72 -14
- package/src/client.ts +11 -0
- package/src/deposit-address.ts +1 -0
- package/src/index.ts +2 -0
- package/src/quote.ts +5 -0
- package/src/transactions.ts +54 -0
- package/src/wallet.ts +4 -1
- package/src/generated/openapi.d.ts +0 -9482
package/dist/quote.js
CHANGED
|
@@ -16,6 +16,7 @@ export function parseClientQuote(value) {
|
|
|
16
16
|
const v = value;
|
|
17
17
|
const confirmations = v["confirmations"];
|
|
18
18
|
const amountCredited = v["amount_credited"];
|
|
19
|
+
const cancelRequestedAt = v["cancel_requested_at"];
|
|
19
20
|
if (typeof v["id"] !== "string" ||
|
|
20
21
|
v["object"] !== "quote" ||
|
|
21
22
|
typeof v["livemode"] !== "boolean" ||
|
|
@@ -31,6 +32,7 @@ export function parseClientQuote(value) {
|
|
|
31
32
|
!/^0x[0-9a-fA-F]{40}$/.test(v["address"]) ||
|
|
32
33
|
typeof v["payment_uri"] !== "string" ||
|
|
33
34
|
!isSafeInteger(v["expires_at"]) ||
|
|
35
|
+
!(cancelRequestedAt === undefined || cancelRequestedAt === null || isSafeInteger(cancelRequestedAt)) ||
|
|
34
36
|
!oneOf(v["payment_status"], PAYMENT_STATUSES) ||
|
|
35
37
|
!(confirmations === null || isSafeInteger(confirmations)) ||
|
|
36
38
|
!(amountCredited === null || isSafeInteger(amountCredited)) ||
|
|
@@ -51,6 +53,7 @@ export function parseClientQuote(value) {
|
|
|
51
53
|
address: v["address"],
|
|
52
54
|
payment_uri: v["payment_uri"],
|
|
53
55
|
expires_at: v["expires_at"],
|
|
56
|
+
...(cancelRequestedAt === undefined ? {} : { cancel_requested_at: cancelRequestedAt }),
|
|
54
57
|
payment_status: v["payment_status"],
|
|
55
58
|
confirmations,
|
|
56
59
|
amount_credited: amountCredited,
|
package/dist/quote.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quote.js","sourceRoot":"","sources":["../src/quote.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"quote.js","sourceRoot":"","sources":["../src/quote.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Hash } from "viem";
|
|
2
|
+
import { type ClientQuote } from "./quote.js";
|
|
3
|
+
export interface SubmitTransactionOptions {
|
|
4
|
+
apiBase: string;
|
|
5
|
+
clientSecret: string;
|
|
6
|
+
transactionHash: Hash;
|
|
7
|
+
/** Required for a deposit-address secret; quotes select their stored chain. */
|
|
8
|
+
chainId?: number;
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
requestTimeout?: number;
|
|
11
|
+
fetch?: typeof globalThis.fetch;
|
|
12
|
+
}
|
|
13
|
+
/** Sends a best-effort detection hint. Receipt of a hint says nothing about payment status. */
|
|
14
|
+
export declare function submitTransaction(options: SubmitTransactionOptions): Promise<void>;
|
|
15
|
+
type QuoteSubmission = Omit<SubmitTransactionOptions, "transactionHash" | "chainId">;
|
|
16
|
+
/** Keep the checkout's origin and secret out of its public, serializable quote view. */
|
|
17
|
+
export declare function bindQuoteSubmission(quote: ClientQuote, options: QuoteSubmission): void;
|
|
18
|
+
/** Automatic wallet hints never delay or change the result of an already broadcast payment. */
|
|
19
|
+
export declare function submitWalletTransaction(quote: ClientQuote, transactionHash: Hash): void;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=transactions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactions.d.ts","sourceRoot":"","sources":["../src/transactions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAA2B,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAGvE,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,IAAI,CAAC;IACtB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAED,+FAA+F;AAC/F,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBxF;AAED,KAAK,eAAe,GAAG,IAAI,CAAC,wBAAwB,EAAE,iBAAiB,GAAG,SAAS,CAAC,CAAC;AAGrF,wFAAwF;AACxF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAEtF;AAED,+FAA+F;AAC/F,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,IAAI,GAAG,IAAI,CAKvF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { quoteIdFromClientSecret } from "./quote.js";
|
|
2
|
+
import { requestSignal, trimTrailingSlashes } from "./request.js";
|
|
3
|
+
/** Sends a best-effort detection hint. Receipt of a hint says nothing about payment status. */
|
|
4
|
+
export async function submitTransaction(options) {
|
|
5
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(options.transactionHash)) {
|
|
6
|
+
throw new TypeError("Invalid transaction hash");
|
|
7
|
+
}
|
|
8
|
+
const addressId = /^(da_[0-9a-f]{32})_secret_/.exec(options.clientSecret)?.[1];
|
|
9
|
+
const id = addressId ?? quoteIdFromClientSecret(options.clientSecret);
|
|
10
|
+
if (addressId !== undefined && (!Number.isSafeInteger(options.chainId) || (options.chainId ?? 0) <= 0)) {
|
|
11
|
+
throw new TypeError("A deposit-address hint requires chainId");
|
|
12
|
+
}
|
|
13
|
+
const resource = addressId === undefined ? "quotes" : "deposit_addresses";
|
|
14
|
+
const url = new URL(`${trimTrailingSlashes(options.apiBase)}/v1/${resource}/${encodeURIComponent(id)}/transactions`);
|
|
15
|
+
url.searchParams.set("client_secret", options.clientSecret);
|
|
16
|
+
const response = await (options.fetch ?? globalThis.fetch)(url, {
|
|
17
|
+
method: "POST",
|
|
18
|
+
headers: { "Content-Type": "application/json" },
|
|
19
|
+
body: JSON.stringify({ transaction_hash: options.transactionHash, ...(addressId === undefined ? {} : { chain_id: options.chainId }) }),
|
|
20
|
+
signal: requestSignal(options),
|
|
21
|
+
credentials: "omit",
|
|
22
|
+
redirect: "error",
|
|
23
|
+
});
|
|
24
|
+
if (response.status !== 202)
|
|
25
|
+
throw new Error("Transaction hint was not received");
|
|
26
|
+
}
|
|
27
|
+
const submissions = new WeakMap();
|
|
28
|
+
/** Keep the checkout's origin and secret out of its public, serializable quote view. */
|
|
29
|
+
export function bindQuoteSubmission(quote, options) {
|
|
30
|
+
submissions.set(quote, options);
|
|
31
|
+
}
|
|
32
|
+
/** Automatic wallet hints never delay or change the result of an already broadcast payment. */
|
|
33
|
+
export function submitWalletTransaction(quote, transactionHash) {
|
|
34
|
+
const options = submissions.get(quote);
|
|
35
|
+
if (options !== undefined) {
|
|
36
|
+
void submitTransaction({ ...options, transactionHash }).catch(() => undefined);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=transactions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactions.js","sourceRoot":"","sources":["../src/transactions.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAoB,MAAM,YAAY,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAalE,+FAA+F;AAC/F,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAiC;IACvE,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,SAAS,CAAC,0BAA0B,CAAC,CAAC;IAClD,CAAC;IACD,MAAM,SAAS,GAAG,4BAA4B,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/E,MAAM,EAAE,GAAG,SAAS,IAAI,uBAAuB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACtE,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACvG,MAAM,IAAI,SAAS,CAAC,yCAAyC,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,QAAQ,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC;IAC1E,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,mBAAmB,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,QAAQ,IAAI,kBAAkB,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC;IACrH,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE;QAC9D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QACtI,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC;QAC9B,WAAW,EAAE,MAAM;QACnB,QAAQ,EAAE,OAAO;KAClB,CAAC,CAAC;IACH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;AACpF,CAAC;AAGD,MAAM,WAAW,GAAG,IAAI,OAAO,EAAgC,CAAC;AAEhE,wFAAwF;AACxF,MAAM,UAAU,mBAAmB,CAAC,KAAkB,EAAE,OAAwB;IAC9E,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AAClC,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,uBAAuB,CAAC,KAAkB,EAAE,eAAqB;IAC/E,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,KAAK,iBAAiB,CAAC,EAAE,GAAG,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACjF,CAAC;AACH,CAAC"}
|
package/dist/wallet.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wallet.d.ts","sourceRoot":"","sources":["../src/wallet.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wallet.d.ts","sourceRoot":"","sources":["../src/wallet.ts"],"names":[],"mappings":"AAEA,OAAO,EAQL,KAAK,IAAI,EACT,KAAK,YAAY,EAClB,MAAM,MAAM,CAAC;AAKd,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,8BAA8B;AAC9B,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,2FAA2F;AAC3F,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACvE;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAED,iGAAiG;AACjG,eAAO,MAAM,oBAAoB,aAAa,CAAC;AAE/C;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAa9E;AA8BD,MAAM,MAAM,eAAe,GACvB,UAAU,GACV,YAAY,GACZ,aAAa,GACb,sBAAsB,GACtB,QAAQ,CAAC;AAEb,qBAAa,WAAY,SAAQ,KAAK;IAIlC,QAAQ,CAAC,IAAI,EAAE,eAAe;IAHhC,SAAkB,IAAI,iBAAiB;gBAG5B,IAAI,EAAE,eAAe,EAC9B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,YAAY;CAIzB;AAED;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,YAAY,GAAG,gBAAgB,EACvC,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,IAAI,CAAC,CAsCf"}
|
package/dist/wallet.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { submitWalletTransaction } from "./transactions.js";
|
|
1
2
|
import { createStore } from "mipd";
|
|
2
3
|
import { BaseError, createWalletClient, custom, erc20Abi, SwitchChainError, UserRejectedRequestError, } from "viem";
|
|
3
4
|
import { readContract } from "viem/actions";
|
|
@@ -82,7 +83,7 @@ export async function payWithWallet(wallet, quote) {
|
|
|
82
83
|
throw new WalletError("insufficient_balance", `Your wallet holds ${formatUnitsGrouped(balance, quote.decimals)} ${symbol}, less than the ` +
|
|
83
84
|
`${formatTokenAmount(quote)} ${symbol} to pay. Nothing was sent.`);
|
|
84
85
|
}
|
|
85
|
-
|
|
86
|
+
const hash = await client.writeContract({
|
|
86
87
|
account,
|
|
87
88
|
chain: knownChain(transfer.chainId) ?? null,
|
|
88
89
|
address: transfer.token,
|
|
@@ -90,6 +91,8 @@ export async function payWithWallet(wallet, quote) {
|
|
|
90
91
|
functionName: "transfer",
|
|
91
92
|
args: [transfer.to, transfer.amount],
|
|
92
93
|
});
|
|
94
|
+
submitWalletTransaction(quote, hash);
|
|
95
|
+
return hash;
|
|
93
96
|
}
|
|
94
97
|
catch (error) {
|
|
95
98
|
if (error instanceof WalletError) {
|
package/dist/wallet.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wallet.js","sourceRoot":"","sources":["../src/wallet.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"wallet.js","sourceRoot":"","sources":["../src/wallet.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phala/pay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Phala Pay framework-free browser client",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"crypto",
|
|
@@ -52,10 +52,7 @@
|
|
|
52
52
|
"test": "vitest run",
|
|
53
53
|
"e2e": "playwright test",
|
|
54
54
|
"e2e:docker": "e2e/docker.sh",
|
|
55
|
-
"check": "pnpm run typecheck && pnpm run lint && pnpm run test && pnpm run build
|
|
56
|
-
"generate:types": "openapi-typescript ../../crates/topup/openapi.json -o src/generated/openapi.d.ts --immutable",
|
|
57
|
-
"check-generated": "bash ../scripts/check-generated-js.sh",
|
|
58
|
-
"test:fixtures": "pnpm --filter @phala/pay-server run test -- --run test/shared-fixtures.test.ts test/merchant-pins-ledger.test.ts test/merchant-transport.test.ts"
|
|
55
|
+
"check": "pnpm run typecheck && pnpm run lint && pnpm run test && pnpm run build"
|
|
59
56
|
},
|
|
60
57
|
"dependencies": {
|
|
61
58
|
"mipd": "^0.0.7"
|
|
@@ -78,7 +75,6 @@
|
|
|
78
75
|
"viem": "^2.57.2",
|
|
79
76
|
"vite": "^8.3.2",
|
|
80
77
|
"vitest": "^5.0.3",
|
|
81
|
-
"openapi-typescript": "7.13.0",
|
|
82
78
|
"@types/react": "^19.3.0",
|
|
83
79
|
"@types/react-dom": "^19.3.0",
|
|
84
80
|
"@vitejs/plugin-react": "^6.1.1",
|
package/src/checkout.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bindQuoteSubmission } from "./transactions.js";
|
|
1
2
|
import { requestSignal, trimTrailingSlashes } from "./request.js";
|
|
2
3
|
import { getAddress, isAddress, isAddressEqual } from "viem";
|
|
3
4
|
import { parseClientQuote, quoteIdFromClientSecret, type ClientQuote } from "./quote.js";
|
|
@@ -37,6 +38,8 @@ export type CheckoutErrorCode =
|
|
|
37
38
|
| "api_error";
|
|
38
39
|
|
|
39
40
|
export interface CheckoutErrorOptions extends ErrorOptions {
|
|
41
|
+
/** The HTTP status, when the error came from a response. */
|
|
42
|
+
status?: number;
|
|
40
43
|
/** The failed response's `Request-Id`, `req_…`, to quote to support. */
|
|
41
44
|
requestId?: string;
|
|
42
45
|
/** For `rate_limited`: the seconds the service asked to wait (`Retry-After`). */
|
|
@@ -45,6 +48,8 @@ export interface CheckoutErrorOptions extends ErrorOptions {
|
|
|
45
48
|
|
|
46
49
|
export class CheckoutError extends Error {
|
|
47
50
|
override readonly name = "CheckoutError";
|
|
51
|
+
/** The HTTP status, when the error came from a response. */
|
|
52
|
+
readonly status?: number;
|
|
48
53
|
/** The failed response's `Request-Id`, when the service answered. */
|
|
49
54
|
readonly requestId: string | undefined;
|
|
50
55
|
/** For `rate_limited`: the seconds to wait before the next read. */
|
|
@@ -56,46 +61,60 @@ export class CheckoutError extends Error {
|
|
|
56
61
|
options?: CheckoutErrorOptions,
|
|
57
62
|
) {
|
|
58
63
|
super(message, options);
|
|
64
|
+
if (options?.status !== undefined) {
|
|
65
|
+
this.status = options.status;
|
|
66
|
+
}
|
|
59
67
|
this.requestId = options?.requestId;
|
|
60
68
|
this.retryAfter = options?.retryAfter;
|
|
61
69
|
}
|
|
62
70
|
}
|
|
63
71
|
|
|
64
72
|
/**
|
|
65
|
-
* The error of a failed read of a public view, with the response's `Request-Id`
|
|
66
|
-
* its `Retry-After`.
|
|
73
|
+
* The error of a failed read of a public view, with the response's HTTP status, `Request-Id`
|
|
74
|
+
* and, on `429`, its `Retry-After`.
|
|
67
75
|
*/
|
|
68
76
|
export function responseError(response: Response, notFound: string): CheckoutError {
|
|
69
77
|
const header = response.headers.get("request-id");
|
|
70
|
-
const
|
|
78
|
+
const options = { status: response.status, ...(header === null ? {} : { requestId: header }) };
|
|
71
79
|
if (response.status === 404) {
|
|
72
|
-
return new CheckoutError("invalid_client_secret", notFound,
|
|
80
|
+
return new CheckoutError("invalid_client_secret", notFound, options);
|
|
73
81
|
}
|
|
74
82
|
if (response.status >= 500) {
|
|
75
83
|
const seconds = Number(response.headers.get("retry-after"));
|
|
76
84
|
return new CheckoutError("service_unavailable", "the payment service is temporarily unavailable", {
|
|
77
|
-
...
|
|
85
|
+
...options,
|
|
78
86
|
...(Number.isFinite(seconds) && seconds > 0 ? { retryAfter: seconds } : {}),
|
|
79
87
|
});
|
|
80
88
|
}
|
|
81
89
|
if (response.status === 429) {
|
|
82
90
|
const seconds = Number(response.headers.get("retry-after"));
|
|
83
91
|
return new CheckoutError("rate_limited", "too many status requests", {
|
|
84
|
-
...
|
|
92
|
+
...options,
|
|
85
93
|
...(Number.isFinite(seconds) && seconds > 0 ? { retryAfter: seconds } : {}),
|
|
86
94
|
});
|
|
87
95
|
}
|
|
88
96
|
return new CheckoutError(
|
|
89
97
|
"api_error",
|
|
90
98
|
`the payment service answered ${response.status}`,
|
|
91
|
-
|
|
99
|
+
options,
|
|
92
100
|
);
|
|
93
101
|
}
|
|
94
102
|
|
|
95
|
-
/**
|
|
96
|
-
|
|
103
|
+
/**
|
|
104
|
+
* The next read's delay with uniform ±20% jitter capped at MAX_BACKOFF after jitter.
|
|
105
|
+
* Retry-After remains a minimum, even when it asks for longer than the backoff cap.
|
|
106
|
+
* `random` defaults to Math.random and can be injected for deterministic tests.
|
|
107
|
+
*/
|
|
108
|
+
export function pollDelay(
|
|
109
|
+
interval: number,
|
|
110
|
+
failures: number,
|
|
111
|
+
error: CheckoutError | null,
|
|
112
|
+
random: () => number = Math.random,
|
|
113
|
+
): number {
|
|
97
114
|
const backoff = failures === 0 ? interval : Math.min(interval * 2 ** failures, MAX_BACKOFF);
|
|
98
|
-
|
|
115
|
+
const retryAfter = (error?.retryAfter ?? 0) * 1000;
|
|
116
|
+
const jittered = Math.max(backoff, retryAfter) * (0.8 + 0.4 * random());
|
|
117
|
+
return Math.max(Math.min(jittered, MAX_BACKOFF), retryAfter);
|
|
99
118
|
}
|
|
100
119
|
|
|
101
120
|
export interface CheckoutState {
|
|
@@ -180,14 +199,21 @@ export async function retrieveQuote(options: RetrieveQuoteOptions): Promise<Clie
|
|
|
180
199
|
} catch (cause) {
|
|
181
200
|
throw new CheckoutError("invalid_response", "unexpected response from the payment service", {
|
|
182
201
|
cause,
|
|
202
|
+
status: response.status,
|
|
183
203
|
});
|
|
184
204
|
}
|
|
185
205
|
if (!isAddressEqual(getAddress(quote.address), expected)) {
|
|
186
206
|
throw new CheckoutError(
|
|
187
207
|
"address_mismatch",
|
|
188
208
|
"the quote's address is not the one the merchant expects",
|
|
209
|
+
{ status: response.status },
|
|
189
210
|
);
|
|
190
211
|
}
|
|
212
|
+
bindQuoteSubmission(quote, {
|
|
213
|
+
apiBase: options.apiBase, clientSecret: options.clientSecret,
|
|
214
|
+
...(options.fetch === undefined ? {} : { fetch: options.fetch }),
|
|
215
|
+
...(options.requestTimeout === undefined ? {} : { requestTimeout: options.requestTimeout }),
|
|
216
|
+
});
|
|
191
217
|
return quote;
|
|
192
218
|
}
|
|
193
219
|
|
|
@@ -229,10 +255,12 @@ export function createCheckout(options: CheckoutOptions): CheckoutSession {
|
|
|
229
255
|
const now = options.now ?? Date.now;
|
|
230
256
|
const interval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;
|
|
231
257
|
const listeners = new Set<(state: CheckoutState) => void>();
|
|
258
|
+
const visibilityDocument = typeof document === "undefined" ? undefined : document;
|
|
232
259
|
|
|
233
260
|
let state: CheckoutState = { status: "loading", quote: null, error: null };
|
|
234
261
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
235
262
|
let failures = 0;
|
|
263
|
+
let clientFailures = 0;
|
|
236
264
|
let lastFailure: CheckoutError | null = null;
|
|
237
265
|
let destroyed = false;
|
|
238
266
|
let inFlight: Promise<void> | undefined;
|
|
@@ -273,6 +301,7 @@ export function createCheckout(options: CheckoutOptions): CheckoutSession {
|
|
|
273
301
|
return;
|
|
274
302
|
}
|
|
275
303
|
failures = 0;
|
|
304
|
+
clientFailures = 0;
|
|
276
305
|
lastFailure = null;
|
|
277
306
|
setState({ status: checkoutStatus(quote, now() / 1000), quote, error: null });
|
|
278
307
|
} catch (cause) {
|
|
@@ -284,6 +313,14 @@ export function createCheckout(options: CheckoutOptions): CheckoutSession {
|
|
|
284
313
|
? cause
|
|
285
314
|
: new CheckoutError("network_error", "could not reach the payment service", { cause });
|
|
286
315
|
failures += 1;
|
|
316
|
+
clientFailures =
|
|
317
|
+
error.status !== undefined &&
|
|
318
|
+
error.status >= 400 &&
|
|
319
|
+
error.status < 500 &&
|
|
320
|
+
error.status !== 408 &&
|
|
321
|
+
error.status !== 429
|
|
322
|
+
? clientFailures + 1
|
|
323
|
+
: 0;
|
|
287
324
|
lastFailure = error;
|
|
288
325
|
const quote = state.quote;
|
|
289
326
|
if (error.code === "address_mismatch") {
|
|
@@ -291,6 +328,8 @@ export function createCheckout(options: CheckoutOptions): CheckoutSession {
|
|
|
291
328
|
setState({ status: "error", quote: null, error });
|
|
292
329
|
} else if (error.code === "invalid_client_secret") {
|
|
293
330
|
setState({ status: "error", quote, error });
|
|
331
|
+
} else if (clientFailures >= 3) {
|
|
332
|
+
setState({ status: "error", quote, error });
|
|
294
333
|
} else if (error.code === "network_error" || error.code === "service_unavailable") {
|
|
295
334
|
// Do not infer expiry or discard the payer's view while chain progress is unknown.
|
|
296
335
|
setState({ status: state.status, quote, error: null, reconnecting: true });
|
|
@@ -315,16 +354,35 @@ export function createCheckout(options: CheckoutOptions): CheckoutSession {
|
|
|
315
354
|
}
|
|
316
355
|
|
|
317
356
|
function schedule(): void {
|
|
318
|
-
|
|
357
|
+
clearTimeout(timer);
|
|
358
|
+
if (finished() || visibilityDocument?.visibilityState === "hidden") {
|
|
319
359
|
return;
|
|
320
360
|
}
|
|
321
361
|
const delay = pollDelay(interval, failures, lastFailure);
|
|
322
362
|
timer = setTimeout(() => {
|
|
323
|
-
|
|
363
|
+
if (!finished() && visibilityDocument?.visibilityState !== "hidden") {
|
|
364
|
+
void refresh().then(schedule);
|
|
365
|
+
}
|
|
324
366
|
}, delay);
|
|
325
367
|
}
|
|
326
368
|
|
|
327
|
-
|
|
369
|
+
function visibilityChanged(): void {
|
|
370
|
+
clearTimeout(timer);
|
|
371
|
+
if (visibilityDocument?.visibilityState !== "hidden" && !finished()) {
|
|
372
|
+
void refresh().then(schedule);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function stopPolling(): void {
|
|
377
|
+
clearTimeout(timer);
|
|
378
|
+
visibilityDocument?.removeEventListener("visibilitychange", visibilityChanged);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
visibilityDocument?.addEventListener("visibilitychange", visibilityChanged);
|
|
382
|
+
signal.addEventListener("abort", stopPolling, { once: true });
|
|
383
|
+
if (visibilityDocument?.visibilityState !== "hidden") {
|
|
384
|
+
void refresh().then(schedule);
|
|
385
|
+
}
|
|
328
386
|
|
|
329
387
|
return {
|
|
330
388
|
getState: () => state,
|
|
@@ -338,7 +396,7 @@ export function createCheckout(options: CheckoutOptions): CheckoutSession {
|
|
|
338
396
|
destroy() {
|
|
339
397
|
destroyed = true;
|
|
340
398
|
controller.abort();
|
|
341
|
-
|
|
399
|
+
stopPolling();
|
|
342
400
|
listeners.clear();
|
|
343
401
|
},
|
|
344
402
|
};
|
package/src/client.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Hash } from "viem";
|
|
2
|
+
import { submitTransaction, type SubmitTransactionOptions } from "./transactions.js";
|
|
1
3
|
import {
|
|
2
4
|
createCheckout,
|
|
3
5
|
retrieveQuote,
|
|
@@ -60,6 +62,15 @@ export class PhalaPay {
|
|
|
60
62
|
});
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
/** Acknowledges a hint only; continue polling the object for payment status. */
|
|
66
|
+
submitTransaction(
|
|
67
|
+
clientSecret: string,
|
|
68
|
+
transactionHash: Hash,
|
|
69
|
+
options: Pick<SubmitTransactionOptions, "chainId" | "signal" | "requestTimeout"> = {},
|
|
70
|
+
): Promise<void> {
|
|
71
|
+
return submitTransaction({ ...options, clientSecret, transactionHash, apiBase: this.apiBase, ...this.#fetchOption() });
|
|
72
|
+
}
|
|
73
|
+
|
|
63
74
|
#fetchOption(): { fetch?: typeof globalThis.fetch } {
|
|
64
75
|
return this.#fetch === undefined ? {} : { fetch: this.#fetch };
|
|
65
76
|
}
|
package/src/deposit-address.ts
CHANGED
package/src/index.ts
CHANGED
package/src/quote.ts
CHANGED
|
@@ -23,6 +23,8 @@ export interface ClientQuote {
|
|
|
23
23
|
payment_uri: string;
|
|
24
24
|
/** Unix seconds; after this the address must no longer be shown. */
|
|
25
25
|
expires_at: number;
|
|
26
|
+
/** Unix seconds of a cancellation request; the quote stays open until dual coverage closes it. */
|
|
27
|
+
cancel_requested_at?: number | null;
|
|
26
28
|
/**
|
|
27
29
|
* `seen` once in a block (a reorg can remove it), `confirming` while a payment at the route's
|
|
28
30
|
* confirmation is valued and screened, `credited` once it is credited (typically
|
|
@@ -66,6 +68,7 @@ export function parseClientQuote(value: unknown): ClientQuote {
|
|
|
66
68
|
const v = value as Record<string, unknown>;
|
|
67
69
|
const confirmations = v["confirmations"];
|
|
68
70
|
const amountCredited = v["amount_credited"];
|
|
71
|
+
const cancelRequestedAt = v["cancel_requested_at"];
|
|
69
72
|
if (
|
|
70
73
|
typeof v["id"] !== "string" ||
|
|
71
74
|
v["object"] !== "quote" ||
|
|
@@ -82,6 +85,7 @@ export function parseClientQuote(value: unknown): ClientQuote {
|
|
|
82
85
|
!/^0x[0-9a-fA-F]{40}$/.test(v["address"]) ||
|
|
83
86
|
typeof v["payment_uri"] !== "string" ||
|
|
84
87
|
!isSafeInteger(v["expires_at"]) ||
|
|
88
|
+
!(cancelRequestedAt === undefined || cancelRequestedAt === null || isSafeInteger(cancelRequestedAt)) ||
|
|
85
89
|
!oneOf(v["payment_status"], PAYMENT_STATUSES) ||
|
|
86
90
|
!(confirmations === null || isSafeInteger(confirmations)) ||
|
|
87
91
|
!(amountCredited === null || isSafeInteger(amountCredited)) ||
|
|
@@ -103,6 +107,7 @@ export function parseClientQuote(value: unknown): ClientQuote {
|
|
|
103
107
|
address: v["address"],
|
|
104
108
|
payment_uri: v["payment_uri"],
|
|
105
109
|
expires_at: v["expires_at"],
|
|
110
|
+
...(cancelRequestedAt === undefined ? {} : { cancel_requested_at: cancelRequestedAt }),
|
|
106
111
|
payment_status: v["payment_status"],
|
|
107
112
|
confirmations,
|
|
108
113
|
amount_credited: amountCredited,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Hash } from "viem";
|
|
2
|
+
import { quoteIdFromClientSecret, type ClientQuote } from "./quote.js";
|
|
3
|
+
import { requestSignal, trimTrailingSlashes } from "./request.js";
|
|
4
|
+
|
|
5
|
+
export interface SubmitTransactionOptions {
|
|
6
|
+
apiBase: string;
|
|
7
|
+
clientSecret: string;
|
|
8
|
+
transactionHash: Hash;
|
|
9
|
+
/** Required for a deposit-address secret; quotes select their stored chain. */
|
|
10
|
+
chainId?: number;
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
requestTimeout?: number;
|
|
13
|
+
fetch?: typeof globalThis.fetch;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Sends a best-effort detection hint. Receipt of a hint says nothing about payment status. */
|
|
17
|
+
export async function submitTransaction(options: SubmitTransactionOptions): Promise<void> {
|
|
18
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(options.transactionHash)) {
|
|
19
|
+
throw new TypeError("Invalid transaction hash");
|
|
20
|
+
}
|
|
21
|
+
const addressId = /^(da_[0-9a-f]{32})_secret_/.exec(options.clientSecret)?.[1];
|
|
22
|
+
const id = addressId ?? quoteIdFromClientSecret(options.clientSecret);
|
|
23
|
+
if (addressId !== undefined && (!Number.isSafeInteger(options.chainId) || (options.chainId ?? 0) <= 0)) {
|
|
24
|
+
throw new TypeError("A deposit-address hint requires chainId");
|
|
25
|
+
}
|
|
26
|
+
const resource = addressId === undefined ? "quotes" : "deposit_addresses";
|
|
27
|
+
const url = new URL(`${trimTrailingSlashes(options.apiBase)}/v1/${resource}/${encodeURIComponent(id)}/transactions`);
|
|
28
|
+
url.searchParams.set("client_secret", options.clientSecret);
|
|
29
|
+
const response = await (options.fetch ?? globalThis.fetch)(url, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: { "Content-Type": "application/json" },
|
|
32
|
+
body: JSON.stringify({ transaction_hash: options.transactionHash, ...(addressId === undefined ? {} : { chain_id: options.chainId }) }),
|
|
33
|
+
signal: requestSignal(options),
|
|
34
|
+
credentials: "omit",
|
|
35
|
+
redirect: "error",
|
|
36
|
+
});
|
|
37
|
+
if (response.status !== 202) throw new Error("Transaction hint was not received");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type QuoteSubmission = Omit<SubmitTransactionOptions, "transactionHash" | "chainId">;
|
|
41
|
+
const submissions = new WeakMap<ClientQuote, QuoteSubmission>();
|
|
42
|
+
|
|
43
|
+
/** Keep the checkout's origin and secret out of its public, serializable quote view. */
|
|
44
|
+
export function bindQuoteSubmission(quote: ClientQuote, options: QuoteSubmission): void {
|
|
45
|
+
submissions.set(quote, options);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Automatic wallet hints never delay or change the result of an already broadcast payment. */
|
|
49
|
+
export function submitWalletTransaction(quote: ClientQuote, transactionHash: Hash): void {
|
|
50
|
+
const options = submissions.get(quote);
|
|
51
|
+
if (options !== undefined) {
|
|
52
|
+
void submitTransaction({ ...options, transactionHash }).catch(() => undefined);
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/wallet.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { submitWalletTransaction } from "./transactions.js";
|
|
1
2
|
import { createStore } from "mipd";
|
|
2
3
|
import {
|
|
3
4
|
BaseError,
|
|
@@ -136,7 +137,7 @@ export async function payWithWallet(
|
|
|
136
137
|
`${formatTokenAmount(quote)} ${symbol} to pay. Nothing was sent.`,
|
|
137
138
|
);
|
|
138
139
|
}
|
|
139
|
-
|
|
140
|
+
const hash = await client.writeContract({
|
|
140
141
|
account,
|
|
141
142
|
chain: knownChain(transfer.chainId) ?? null,
|
|
142
143
|
address: transfer.token,
|
|
@@ -144,6 +145,8 @@ export async function payWithWallet(
|
|
|
144
145
|
functionName: "transfer",
|
|
145
146
|
args: [transfer.to, transfer.amount],
|
|
146
147
|
});
|
|
148
|
+
submitWalletTransaction(quote, hash);
|
|
149
|
+
return hash;
|
|
147
150
|
} catch (error) {
|
|
148
151
|
if (error instanceof WalletError) {
|
|
149
152
|
throw error;
|