@proxy-checkout/server-js 0.0.1
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/LICENSE +21 -0
- package/README.md +54 -0
- package/dist/client.d.ts +8 -0
- package/dist/client.js +18 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.js +36 -0
- package/dist/http-client.d.ts +16 -0
- package/dist/http-client.js +78 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/sessions.d.ts +72 -0
- package/dist/sessions.js +144 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.js +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.js +3 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Linus Labs, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# @proxy-checkout/server-js
|
|
2
|
+
|
|
3
|
+
Server-side JavaScript SDK for merchant backends calling Proxy merchant APIs.
|
|
4
|
+
|
|
5
|
+
## Exposed API Endpoints
|
|
6
|
+
|
|
7
|
+
This package should expose the merchant-authenticated backend routes that exist today:
|
|
8
|
+
|
|
9
|
+
| SDK method | HTTP endpoint | Purpose |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| `proxy.sessions.create` | `POST /proxy_sessions` | Create a Proxy Elements session. |
|
|
12
|
+
| `proxy.sessions.cart.set` | `PUT /proxy_sessions/:id/cart` | Replace the current cart snapshot before payment orchestration. |
|
|
13
|
+
| `proxy.sessions.payerHandoff` | `POST /proxy_sessions/:id/payer_handoff` | Record merchant handoff issuance before the payer loads checkout. |
|
|
14
|
+
| `proxy.sessions.payerOpened` | `POST /proxy_sessions/:id/payer_opened` | Record checkout open and read the current cart details. |
|
|
15
|
+
|
|
16
|
+
The portal routes are intentionally excluded because they are Better Auth
|
|
17
|
+
session-backed portal APIs. The PSP webhook route is also excluded because PSPs
|
|
18
|
+
call it directly.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { createProxyCheckoutServerClient } from "@proxy-checkout/server-js";
|
|
24
|
+
|
|
25
|
+
const proxy = createProxyCheckoutServerClient({
|
|
26
|
+
apiKey: process.env.PROXY_SECRET_KEY!,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const session = await proxy.sessions.create({
|
|
30
|
+
amountMinor: 5000,
|
|
31
|
+
buyerReference: "buyer_123",
|
|
32
|
+
currency: "usd",
|
|
33
|
+
cartSnapshot: {
|
|
34
|
+
items: [{ product_id: "prod_123", quantity: 1 }],
|
|
35
|
+
},
|
|
36
|
+
idempotencyKey: "order_123",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
await proxy.sessions.payerHandoff(session.id);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Production calls default to `https://api.proxycheckout.com`. Tests, local
|
|
43
|
+
development, and previews can pass `apiHost`.
|
|
44
|
+
|
|
45
|
+
## First Publish Decisions
|
|
46
|
+
|
|
47
|
+
The first public package version is intentionally `0.0.1` because the SDK API is
|
|
48
|
+
early and expected to change. The npm package is MIT-licensed under the
|
|
49
|
+
package-scoped `LICENSE` file.
|
|
50
|
+
|
|
51
|
+
The package metadata is intentionally shaped for a public npm package, but the
|
|
52
|
+
first publish still needs explicit owner approval for:
|
|
53
|
+
|
|
54
|
+
- release automation, provenance, dist tags, and rollback ownership.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ProxySessionsResource } from "./sessions.js";
|
|
2
|
+
import type { ProxyCheckoutServerClientOptions } from "./types.js";
|
|
3
|
+
export declare class ProxyCheckoutServerClient {
|
|
4
|
+
readonly apiHost: string;
|
|
5
|
+
readonly sessions: ProxySessionsResource;
|
|
6
|
+
constructor(options: ProxyCheckoutServerClientOptions);
|
|
7
|
+
}
|
|
8
|
+
export declare function createProxyCheckoutServerClient(options: ProxyCheckoutServerClientOptions): ProxyCheckoutServerClient;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ProxyCheckoutHttpClient } from "./http-client.js";
|
|
2
|
+
import { ProxySessionsResource } from "./sessions.js";
|
|
3
|
+
export class ProxyCheckoutServerClient {
|
|
4
|
+
apiHost;
|
|
5
|
+
sessions;
|
|
6
|
+
constructor(options) {
|
|
7
|
+
const httpClient = new ProxyCheckoutHttpClient({
|
|
8
|
+
apiHost: options.apiHost,
|
|
9
|
+
apiKey: options.apiKey,
|
|
10
|
+
fetchImpl: options.fetch,
|
|
11
|
+
});
|
|
12
|
+
this.apiHost = httpClient.apiHost;
|
|
13
|
+
this.sessions = new ProxySessionsResource(httpClient);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function createProxyCheckoutServerClient(options) {
|
|
17
|
+
return new ProxyCheckoutServerClient(options);
|
|
18
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ProxyCheckoutApiErrorDetails {
|
|
2
|
+
readonly code?: string;
|
|
3
|
+
readonly requestId?: string;
|
|
4
|
+
readonly responseBody: unknown;
|
|
5
|
+
readonly statusCode: number;
|
|
6
|
+
}
|
|
7
|
+
export declare class ProxyCheckoutApiError extends Error {
|
|
8
|
+
readonly name = "ProxyCheckoutApiError";
|
|
9
|
+
readonly code: string | undefined;
|
|
10
|
+
readonly requestId: string | undefined;
|
|
11
|
+
readonly responseBody: unknown;
|
|
12
|
+
readonly statusCode: number;
|
|
13
|
+
constructor(message: string, details: ProxyCheckoutApiErrorDetails);
|
|
14
|
+
}
|
|
15
|
+
export declare function toApiError(statusCode: number, responseBody: unknown, headerRequestId?: string): ProxyCheckoutApiError;
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export class ProxyCheckoutApiError extends Error {
|
|
2
|
+
name = "ProxyCheckoutApiError";
|
|
3
|
+
code;
|
|
4
|
+
requestId;
|
|
5
|
+
responseBody;
|
|
6
|
+
statusCode;
|
|
7
|
+
constructor(message, details) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.code = details.code;
|
|
10
|
+
this.requestId = details.requestId;
|
|
11
|
+
this.responseBody = details.responseBody;
|
|
12
|
+
this.statusCode = details.statusCode;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function toApiError(statusCode, responseBody, headerRequestId) {
|
|
16
|
+
const error = readApiError(responseBody);
|
|
17
|
+
const message = error.message ?? `Proxy API request failed with status ${statusCode}.`;
|
|
18
|
+
return new ProxyCheckoutApiError(message, {
|
|
19
|
+
code: error.code,
|
|
20
|
+
requestId: error.requestId ?? headerRequestId,
|
|
21
|
+
responseBody,
|
|
22
|
+
statusCode,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function readApiError(responseBody) {
|
|
26
|
+
const body = isRecord(responseBody) ? responseBody : undefined;
|
|
27
|
+
const error = body && isRecord(body.error) ? body.error : undefined;
|
|
28
|
+
return {
|
|
29
|
+
code: typeof error?.code === "string" ? error.code : undefined,
|
|
30
|
+
message: typeof error?.message === "string" ? error.message : undefined,
|
|
31
|
+
requestId: typeof error?.request_id === "string" ? error.request_id : undefined,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function isRecord(value) {
|
|
35
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { JsonValue, ProxyCheckoutFetch } from "./types.js";
|
|
2
|
+
export interface ProxyCheckoutRequestOptions {
|
|
3
|
+
readonly idempotencyKey?: string;
|
|
4
|
+
readonly requestId?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class ProxyCheckoutHttpClient {
|
|
7
|
+
readonly apiHost: string;
|
|
8
|
+
private readonly apiKey;
|
|
9
|
+
private readonly fetchImpl;
|
|
10
|
+
constructor({ apiHost, apiKey, fetchImpl, }: {
|
|
11
|
+
apiHost?: string;
|
|
12
|
+
apiKey: string;
|
|
13
|
+
fetchImpl?: ProxyCheckoutFetch;
|
|
14
|
+
});
|
|
15
|
+
request(method: "POST" | "PUT", path: string, body: JsonValue, options?: ProxyCheckoutRequestOptions): Promise<unknown>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { toApiError } from "./errors.js";
|
|
2
|
+
import { proxyCheckoutServerSdkUserAgent } from "./version.js";
|
|
3
|
+
export class ProxyCheckoutHttpClient {
|
|
4
|
+
apiHost;
|
|
5
|
+
apiKey;
|
|
6
|
+
fetchImpl;
|
|
7
|
+
constructor({ apiHost, apiKey, fetchImpl, }) {
|
|
8
|
+
assertSecretKey(apiKey);
|
|
9
|
+
const resolvedFetch = fetchImpl ?? globalThis.fetch;
|
|
10
|
+
if (typeof resolvedFetch !== "function") {
|
|
11
|
+
throw new Error("Proxy Checkout server SDK requires a fetch implementation.");
|
|
12
|
+
}
|
|
13
|
+
this.apiHost = normalizeApiHost(apiHost);
|
|
14
|
+
this.apiKey = apiKey;
|
|
15
|
+
this.fetchImpl = resolvedFetch;
|
|
16
|
+
}
|
|
17
|
+
async request(method, path, body, options = {}) {
|
|
18
|
+
const response = await this.fetchImpl(`${this.apiHost}${path}`, {
|
|
19
|
+
body: JSON.stringify(body),
|
|
20
|
+
headers: buildHeaders(this.apiKey, options),
|
|
21
|
+
method,
|
|
22
|
+
});
|
|
23
|
+
const responseBody = await readResponseBody(response);
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
const headerRequestId = response.headers.get("x-request-id") || undefined;
|
|
26
|
+
throw toApiError(response.status, responseBody, headerRequestId);
|
|
27
|
+
}
|
|
28
|
+
return responseBody;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function buildHeaders(apiKey, options) {
|
|
32
|
+
const headers = {
|
|
33
|
+
accept: "application/json",
|
|
34
|
+
authorization: `Bearer ${apiKey}`,
|
|
35
|
+
"content-type": "application/json",
|
|
36
|
+
"user-agent": proxyCheckoutServerSdkUserAgent,
|
|
37
|
+
};
|
|
38
|
+
if (options.idempotencyKey !== undefined) {
|
|
39
|
+
headers["idempotency-key"] = options.idempotencyKey;
|
|
40
|
+
}
|
|
41
|
+
if (options.requestId !== undefined) {
|
|
42
|
+
headers["x-request-id"] = options.requestId;
|
|
43
|
+
}
|
|
44
|
+
return headers;
|
|
45
|
+
}
|
|
46
|
+
async function readResponseBody(response) {
|
|
47
|
+
const text = await response.text();
|
|
48
|
+
if (!text) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(text);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return text;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function assertSecretKey(apiKey) {
|
|
59
|
+
if (!apiKey.startsWith("sk_test_") && !apiKey.startsWith("sk_live_")) {
|
|
60
|
+
throw new Error("Proxy Checkout secret key must start with sk_test_ or sk_live_.");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function normalizeApiHost(apiHost) {
|
|
64
|
+
if (apiHost === undefined) {
|
|
65
|
+
return "https://api.proxycheckout.com";
|
|
66
|
+
}
|
|
67
|
+
let url;
|
|
68
|
+
try {
|
|
69
|
+
url = new URL(apiHost);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
throw new Error(`Invalid apiHost URL: ${apiHost}. Make sure it is an absolute HTTP or HTTPS URL.`);
|
|
73
|
+
}
|
|
74
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
75
|
+
throw new Error(`Invalid apiHost URL: ${apiHost}. Make sure it is an absolute HTTP or HTTPS URL.`);
|
|
76
|
+
}
|
|
77
|
+
return `${url.origin}${url.pathname}`.replace(/\/$/, "");
|
|
78
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createProxyCheckoutServerClient, ProxyCheckoutServerClient, } from "./client.js";
|
|
2
|
+
export { ProxyCheckoutApiError, type ProxyCheckoutApiErrorDetails } from "./errors.js";
|
|
3
|
+
export { type CreateProxySessionInput, type CreateProxySessionOptions, type PayerHandoffResult, type PayerOpenedResult, type ProxySession, ProxySessionCartResource, type ProxySessionCartResult, ProxySessionsResource, proxyCheckoutServerEndpoints, type SetProxySessionCartInput, } from "./sessions.js";
|
|
4
|
+
export type { JsonObject, JsonPrimitive, JsonValue, ProxyCheckoutFetch, ProxyCheckoutServerClientOptions, ProxyCheckoutServerRequestOptions, } from "./types.js";
|
|
5
|
+
export { proxyCheckoutServerSdkName, proxyCheckoutServerSdkUserAgent, proxyCheckoutServerSdkVersion, } from "./version.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createProxyCheckoutServerClient, ProxyCheckoutServerClient, } from "./client.js";
|
|
2
|
+
export { ProxyCheckoutApiError } from "./errors.js";
|
|
3
|
+
export { ProxySessionCartResource, ProxySessionsResource, proxyCheckoutServerEndpoints, } from "./sessions.js";
|
|
4
|
+
export { proxyCheckoutServerSdkName, proxyCheckoutServerSdkUserAgent, proxyCheckoutServerSdkVersion, } from "./version.js";
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { ProxyCheckoutHttpClient } from "./http-client.js";
|
|
2
|
+
import type { JsonObject, ProxyCheckoutServerRequestOptions } from "./types.js";
|
|
3
|
+
export interface CreateProxySessionOptions extends ProxyCheckoutServerRequestOptions {
|
|
4
|
+
readonly idempotencyKey?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface CreateProxySessionInput extends CreateProxySessionOptions {
|
|
7
|
+
readonly amountMinor: number;
|
|
8
|
+
readonly buyerReference: string;
|
|
9
|
+
readonly cartSnapshot: JsonObject;
|
|
10
|
+
readonly currency: string;
|
|
11
|
+
readonly expiresAt?: Date | string;
|
|
12
|
+
readonly integrationMode?: "elements";
|
|
13
|
+
readonly metadata?: JsonObject;
|
|
14
|
+
readonly payerContact?: {
|
|
15
|
+
readonly email?: string;
|
|
16
|
+
readonly phone?: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface ProxySession {
|
|
20
|
+
readonly expiresAt: string;
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly integrationMode: string;
|
|
23
|
+
readonly status: string;
|
|
24
|
+
}
|
|
25
|
+
export interface PayerOpenedResult {
|
|
26
|
+
readonly amountMinor: number;
|
|
27
|
+
readonly cartDetails: JsonObject;
|
|
28
|
+
readonly currency: string;
|
|
29
|
+
}
|
|
30
|
+
export interface PayerHandoffResult {
|
|
31
|
+
readonly status: "payer_handoff_pending" | "payer_opened";
|
|
32
|
+
}
|
|
33
|
+
export interface SetProxySessionCartInput extends ProxyCheckoutServerRequestOptions {
|
|
34
|
+
readonly amountMinor: number;
|
|
35
|
+
readonly cartSnapshot: JsonObject;
|
|
36
|
+
readonly currency: string;
|
|
37
|
+
}
|
|
38
|
+
export interface ProxySessionCartResult {
|
|
39
|
+
readonly amountMinor: number;
|
|
40
|
+
readonly cartSnapshot: JsonObject;
|
|
41
|
+
readonly currency: string;
|
|
42
|
+
}
|
|
43
|
+
export declare const proxyCheckoutServerEndpoints: readonly [{
|
|
44
|
+
readonly method: "POST";
|
|
45
|
+
readonly operation: "sessions.create";
|
|
46
|
+
readonly path: "/proxy_sessions";
|
|
47
|
+
}, {
|
|
48
|
+
readonly method: "PUT";
|
|
49
|
+
readonly operation: "sessions.cart.set";
|
|
50
|
+
readonly path: "/proxy_sessions/:id/cart";
|
|
51
|
+
}, {
|
|
52
|
+
readonly method: "POST";
|
|
53
|
+
readonly operation: "sessions.payerHandoff";
|
|
54
|
+
readonly path: "/proxy_sessions/:id/payer_handoff";
|
|
55
|
+
}, {
|
|
56
|
+
readonly method: "POST";
|
|
57
|
+
readonly operation: "sessions.payerOpened";
|
|
58
|
+
readonly path: "/proxy_sessions/:id/payer_opened";
|
|
59
|
+
}];
|
|
60
|
+
export declare class ProxySessionsResource {
|
|
61
|
+
private readonly httpClient;
|
|
62
|
+
readonly cart: ProxySessionCartResource;
|
|
63
|
+
constructor(httpClient: ProxyCheckoutHttpClient);
|
|
64
|
+
create(input: CreateProxySessionInput): Promise<ProxySession>;
|
|
65
|
+
payerHandoff(proxySessionId: string, options?: ProxyCheckoutServerRequestOptions): Promise<PayerHandoffResult>;
|
|
66
|
+
payerOpened(proxySessionId: string, options?: ProxyCheckoutServerRequestOptions): Promise<PayerOpenedResult>;
|
|
67
|
+
}
|
|
68
|
+
export declare class ProxySessionCartResource {
|
|
69
|
+
private readonly httpClient;
|
|
70
|
+
constructor(httpClient: ProxyCheckoutHttpClient);
|
|
71
|
+
set(proxySessionId: string, input: SetProxySessionCartInput): Promise<ProxySessionCartResult>;
|
|
72
|
+
}
|
package/dist/sessions.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
export const proxyCheckoutServerEndpoints = [
|
|
2
|
+
{
|
|
3
|
+
method: "POST",
|
|
4
|
+
operation: "sessions.create",
|
|
5
|
+
path: "/proxy_sessions",
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
method: "PUT",
|
|
9
|
+
operation: "sessions.cart.set",
|
|
10
|
+
path: "/proxy_sessions/:id/cart",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
method: "POST",
|
|
14
|
+
operation: "sessions.payerHandoff",
|
|
15
|
+
path: "/proxy_sessions/:id/payer_handoff",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
method: "POST",
|
|
19
|
+
operation: "sessions.payerOpened",
|
|
20
|
+
path: "/proxy_sessions/:id/payer_opened",
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
export class ProxySessionsResource {
|
|
24
|
+
httpClient;
|
|
25
|
+
cart;
|
|
26
|
+
constructor(httpClient) {
|
|
27
|
+
this.httpClient = httpClient;
|
|
28
|
+
this.cart = new ProxySessionCartResource(httpClient);
|
|
29
|
+
}
|
|
30
|
+
async create(input) {
|
|
31
|
+
const response = await this.httpClient.request("POST", "/proxy_sessions", toCreateProxySessionBody(input), {
|
|
32
|
+
idempotencyKey: input.idempotencyKey,
|
|
33
|
+
requestId: input.requestId,
|
|
34
|
+
});
|
|
35
|
+
return toProxySession(response);
|
|
36
|
+
}
|
|
37
|
+
async payerHandoff(proxySessionId, options = {}) {
|
|
38
|
+
const response = await this.httpClient.request("POST", `/proxy_sessions/${encodeURIComponent(proxySessionId)}/payer_handoff`, {}, options);
|
|
39
|
+
const body = requireJsonObject(response, "sessions.payerHandoff");
|
|
40
|
+
return {
|
|
41
|
+
status: requirePayerHandoffStatus(body.status),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async payerOpened(proxySessionId, options = {}) {
|
|
45
|
+
const response = await this.httpClient.request("POST", `/proxy_sessions/${encodeURIComponent(proxySessionId)}/payer_opened`, {}, options);
|
|
46
|
+
const body = requireJsonObject(response, "sessions.payerOpened");
|
|
47
|
+
return {
|
|
48
|
+
amountMinor: requireInteger(body.amount_minor, "sessions.payerOpened.amountMinor"),
|
|
49
|
+
cartDetails: requireJsonObject(body.cart_details, "sessions.payerOpened"),
|
|
50
|
+
currency: requireString(body.currency, "sessions.payerOpened.currency"),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export class ProxySessionCartResource {
|
|
55
|
+
httpClient;
|
|
56
|
+
constructor(httpClient) {
|
|
57
|
+
this.httpClient = httpClient;
|
|
58
|
+
}
|
|
59
|
+
async set(proxySessionId, input) {
|
|
60
|
+
const response = await this.httpClient.request("PUT", `/proxy_sessions/${encodeURIComponent(proxySessionId)}/cart`, toProxySessionCartBody(input), {
|
|
61
|
+
requestId: input.requestId,
|
|
62
|
+
});
|
|
63
|
+
const body = requireJsonObject(response, "sessions.cart.set");
|
|
64
|
+
return {
|
|
65
|
+
amountMinor: requireInteger(body.amount_minor, "sessions.cart.set.amountMinor"),
|
|
66
|
+
cartSnapshot: requireJsonObject(body.cart_snapshot, "sessions.cart.set"),
|
|
67
|
+
currency: requireString(body.currency, "sessions.cart.set.currency"),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function toCreateProxySessionBody(input) {
|
|
72
|
+
const body = {
|
|
73
|
+
amount_minor: input.amountMinor,
|
|
74
|
+
buyer_reference: input.buyerReference,
|
|
75
|
+
cart_snapshot: input.cartSnapshot,
|
|
76
|
+
currency: input.currency,
|
|
77
|
+
};
|
|
78
|
+
if (input.expiresAt !== undefined) {
|
|
79
|
+
body.expires_at =
|
|
80
|
+
input.expiresAt instanceof Date ? input.expiresAt.toISOString() : input.expiresAt;
|
|
81
|
+
}
|
|
82
|
+
if (input.integrationMode !== undefined) {
|
|
83
|
+
body.integration_mode = input.integrationMode;
|
|
84
|
+
}
|
|
85
|
+
if (input.metadata !== undefined) {
|
|
86
|
+
body.metadata = input.metadata;
|
|
87
|
+
}
|
|
88
|
+
if (input.payerContact !== undefined) {
|
|
89
|
+
body.payer_contact = removeUndefinedValues({
|
|
90
|
+
email: input.payerContact.email,
|
|
91
|
+
phone: input.payerContact.phone,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return body;
|
|
95
|
+
}
|
|
96
|
+
function toProxySessionCartBody(input) {
|
|
97
|
+
return {
|
|
98
|
+
amount_minor: input.amountMinor,
|
|
99
|
+
cart_snapshot: input.cartSnapshot,
|
|
100
|
+
currency: input.currency,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function removeUndefinedValues(input) {
|
|
104
|
+
return Object.fromEntries(Object.entries(input).filter((entry) => entry[1] !== undefined));
|
|
105
|
+
}
|
|
106
|
+
function toProxySession(response) {
|
|
107
|
+
const body = requireJsonObject(response, "sessions.create");
|
|
108
|
+
const status = requireString(body.status, "sessions.create.status");
|
|
109
|
+
const integrationMode = requireString(body.integration_mode, "sessions.create.integrationMode");
|
|
110
|
+
return {
|
|
111
|
+
expiresAt: requireString(body.expires_at, "sessions.create.expiresAt"),
|
|
112
|
+
id: requireString(body.id, "sessions.create.id"),
|
|
113
|
+
integrationMode,
|
|
114
|
+
status,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function requireJsonObject(value, operation) {
|
|
118
|
+
if (!isRecord(value)) {
|
|
119
|
+
throw new Error(`Proxy API response for ${operation} must be a JSON object.`);
|
|
120
|
+
}
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
function requireString(value, field) {
|
|
124
|
+
if (typeof value !== "string") {
|
|
125
|
+
throw new Error(`Proxy API response field ${field} must be a string.`);
|
|
126
|
+
}
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
function requireInteger(value, field) {
|
|
130
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
131
|
+
throw new Error(`Proxy API response field ${field} must be an integer.`);
|
|
132
|
+
}
|
|
133
|
+
return value;
|
|
134
|
+
}
|
|
135
|
+
function requirePayerHandoffStatus(value) {
|
|
136
|
+
const status = requireString(value, "sessions.payerHandoff.status");
|
|
137
|
+
if (status === "payer_handoff_pending" || status === "payer_opened") {
|
|
138
|
+
return status;
|
|
139
|
+
}
|
|
140
|
+
throw new Error("Proxy API response field sessions.payerHandoff.status is unsupported.");
|
|
141
|
+
}
|
|
142
|
+
function isRecord(value) {
|
|
143
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
144
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type JsonPrimitive = boolean | null | number | string;
|
|
2
|
+
export type JsonValue = JsonPrimitive | JsonObject | JsonValue[];
|
|
3
|
+
export interface JsonObject {
|
|
4
|
+
readonly [key: string]: JsonValue;
|
|
5
|
+
}
|
|
6
|
+
export type ProxyCheckoutFetch = (input: Request | URL | string, init?: RequestInit) => Promise<Response>;
|
|
7
|
+
export interface ProxyCheckoutServerClientOptions {
|
|
8
|
+
readonly apiHost?: string;
|
|
9
|
+
readonly apiKey: string;
|
|
10
|
+
readonly fetch?: ProxyCheckoutFetch;
|
|
11
|
+
}
|
|
12
|
+
export interface ProxyCheckoutServerRequestOptions {
|
|
13
|
+
readonly requestId?: string;
|
|
14
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/version.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Server-side JavaScript SDK for Proxy merchant API calls.",
|
|
3
|
+
"license": "MIT",
|
|
4
|
+
"module": "./dist/index.js",
|
|
5
|
+
"name": "@proxy-checkout/server-js",
|
|
6
|
+
"private": false,
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"type": "module",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"version": "0.0.1",
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@types/node": "^24.12.4",
|
|
13
|
+
"@vitest/coverage-v8": "4.1.7",
|
|
14
|
+
"typescript": "^6.0.3",
|
|
15
|
+
"vitest": "4.1.7"
|
|
16
|
+
},
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"keywords": [
|
|
29
|
+
"proxy-checkout",
|
|
30
|
+
"checkout",
|
|
31
|
+
"payments",
|
|
32
|
+
"sdk"
|
|
33
|
+
],
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"directory": "sdks/server-js",
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/linuslabs/proxy.git"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc -p tsconfig.build.json",
|
|
44
|
+
"test:all": "vitest run",
|
|
45
|
+
"test:coverage": "vitest run --coverage.enabled --coverage.reportsDirectory=coverage/all",
|
|
46
|
+
"test:coverage:changed": "vitest run --coverage.enabled --coverage.reportsDirectory=coverage/changed",
|
|
47
|
+
"test:coverage:integration": "vitest run --project integration --coverage.enabled --coverage.reportsDirectory=coverage/integration",
|
|
48
|
+
"test:coverage:unit": "vitest run --project unit --coverage.enabled --coverage.reportsDirectory=coverage/unit",
|
|
49
|
+
"test:integration": "vitest run --project integration",
|
|
50
|
+
"test:unit": "vitest run --project unit",
|
|
51
|
+
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
52
|
+
}
|
|
53
|
+
}
|