framepayments 2.3.1 → 2.4.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 +36 -0
- package/dist/index.cjs +81 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -12
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +38 -12
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +81 -54
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -83,6 +83,42 @@ await frame.configuration.getEvervaultConfiguration({ usePublishableKey: true })
|
|
|
83
83
|
|
|
84
84
|
The SDK defaults match the native Frame iOS / Android SDKs exactly — endpoints that those SDKs invoke with `usePublishableKey: true` are publishable-by-default here as well (`paymentMethods.createApplePayPaymentMethod`, `paymentMethods.createGooglePayPaymentMethod`, `deviceAttestation.*`, `wallet.getGooglePayConfiguration`). Everything else defaults to secret.
|
|
85
85
|
|
|
86
|
+
## 🪪 Onboarding sessions & per-object client secrets
|
|
87
|
+
|
|
88
|
+
Publishable-key-only mobile clients can authenticate **onboarding sessions** and **per-object client secrets**, matching the three-tier auth model of the native Frame iOS / Android SDKs. The bearer for any request is resolved in this precedence order:
|
|
89
|
+
|
|
90
|
+
1. a per-request `authToken` (an object `client_secret`, e.g. `ci_..._secret_...`), else
|
|
91
|
+
2. the active onboarding-session token (e.g. `onb_sess_...`), else
|
|
92
|
+
3. the publishable key (when `usePublishableKey: true`) or the secret key.
|
|
93
|
+
|
|
94
|
+
### Onboarding sessions
|
|
95
|
+
|
|
96
|
+
Begin a session to route **every** request through the session token, overriding the configured keys (and ignoring `usePublishableKey`) while it is active. Mirrors iOS `beginOnboardingSession` / `endOnboardingSession`.
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
const frame = new FrameSDK({ publishableKey: 'pk_...' });
|
|
100
|
+
|
|
101
|
+
frame.setOnboardingSession('onb_sess_...');
|
|
102
|
+
// Every call now sends `Authorization: Bearer onb_sess_...`.
|
|
103
|
+
await frame.onboardingSessions.create({ /* ... */ });
|
|
104
|
+
await frame.termsOfService.createToken();
|
|
105
|
+
|
|
106
|
+
// End the session, reverting to the publishable key.
|
|
107
|
+
frame.clearOnboardingSession();
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`clearOnboardingSession(token?)` is **safe-clear**: passing a `token` only clears the session when it matches the active one (so a stale unmount can't wipe a newer session), and returns `true` if it cleared / `false` if the guard prevented it. Omit the argument to force-clear.
|
|
111
|
+
|
|
112
|
+
### Per-object client secrets
|
|
113
|
+
|
|
114
|
+
For charge-intent confirm/show and 3-D Secure, pass the object's `client_secret` as a per-request `authToken`. It wins over both an active session and the configured keys:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
await frame.chargeIntents.confirm('ci_123', { authToken: 'ci_123_secret_...' });
|
|
118
|
+
await frame.chargeIntents.get('ci_123', { authToken: 'ci_123_secret_...' });
|
|
119
|
+
await frame.threeDS.get('tds_123', { authToken: 'tds_123_secret_...' });
|
|
120
|
+
```
|
|
121
|
+
|
|
86
122
|
## 📚 Available APIs
|
|
87
123
|
|
|
88
124
|
| Namespace | Purpose |
|
package/dist/index.cjs
CHANGED
|
@@ -35,12 +35,14 @@ var FrameAPIError = class extends Error {
|
|
|
35
35
|
};
|
|
36
36
|
//#endregion
|
|
37
37
|
//#region src/client.ts
|
|
38
|
-
const
|
|
38
|
+
const FRAME_USE_PUBLISHABLE_KEY = "frameUsePublishableKey";
|
|
39
|
+
const FRAME_AUTH_TOKEN = "frameAuthToken";
|
|
40
|
+
const createOnboardingSessionStore = () => ({ token: null });
|
|
39
41
|
function safeRawFromAxiosError(err) {
|
|
40
42
|
if (!err || typeof err !== "object") return err;
|
|
41
43
|
const cleanedConfig = err.config ? (() => {
|
|
42
|
-
const { headers, ...restConfig } = err.config;
|
|
43
|
-
const cleanedHeaders = headers && typeof headers === "object" ? Object.fromEntries(Object.entries(headers).filter(([k]) => k.toLowerCase() !== "authorization"
|
|
44
|
+
const { headers, [FRAME_AUTH_TOKEN]: _authToken, ...restConfig } = err.config;
|
|
45
|
+
const cleanedHeaders = headers && typeof headers === "object" ? Object.fromEntries(Object.entries(headers).filter(([k]) => k.toLowerCase() !== "authorization")) : headers;
|
|
44
46
|
return {
|
|
45
47
|
...restConfig,
|
|
46
48
|
headers: cleanedHeaders
|
|
@@ -53,7 +55,7 @@ function safeRawFromAxiosError(err) {
|
|
|
53
55
|
config: cleanedConfig
|
|
54
56
|
};
|
|
55
57
|
}
|
|
56
|
-
const createApiClient = (config) => {
|
|
58
|
+
const createApiClient = (config, sessionStore = createOnboardingSessionStore()) => {
|
|
57
59
|
const { apiKey, publishableKey, defaultHeaders } = config;
|
|
58
60
|
if (!apiKey && !publishableKey) throw new Error("FrameSDK config requires at least one of: apiKey, publishableKey. Mobile clients should ship publishableKey only; server code should use apiKey.");
|
|
59
61
|
const baseURL = config.baseURL ?? "https://api.framepayments.com";
|
|
@@ -63,9 +65,11 @@ const createApiClient = (config) => {
|
|
|
63
65
|
});
|
|
64
66
|
client.interceptors.request.use((requestConfig) => {
|
|
65
67
|
const headers = requestConfig.headers;
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
const cfg = requestConfig;
|
|
69
|
+
const wantsPublishable = cfg.frameUsePublishableKey === true;
|
|
70
|
+
const rawAuthToken = cfg.frameAuthToken;
|
|
71
|
+
if (rawAuthToken === "") throw new FrameAPIError("Frame authToken was provided but empty. Pass a non-empty client_secret (e.g. ci_..._secret_...), or omit authToken to use the configured key.", "invalid_auth_token", 0, null);
|
|
72
|
+
const perRequestAuthToken = typeof rawAuthToken === "string" && rawAuthToken.length > 0 ? rawAuthToken : void 0;
|
|
69
73
|
if (defaultHeaders) for (const [name, value] of Object.entries(defaultHeaders)) {
|
|
70
74
|
if (value == null) continue;
|
|
71
75
|
if (headers instanceof axios.AxiosHeaders) {
|
|
@@ -75,10 +79,17 @@ const createApiClient = (config) => {
|
|
|
75
79
|
if (h[name] === void 0) h[name] = value;
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
if (
|
|
81
|
-
else if (
|
|
82
|
+
const sessionToken = sessionStore.token;
|
|
83
|
+
let bearer;
|
|
84
|
+
if (perRequestAuthToken) bearer = perRequestAuthToken;
|
|
85
|
+
else if (sessionToken) bearer = sessionToken;
|
|
86
|
+
else {
|
|
87
|
+
const keyToUse = wantsPublishable ? publishableKey : apiKey;
|
|
88
|
+
if (!keyToUse) throw new FrameAPIError(wantsPublishable ? "Frame publishable key is not configured. Pass { publishableKey } to new FrameSDK(...) before calling endpoints with { usePublishableKey: true }." : "Frame API key is not configured. Pass { apiKey } to new FrameSDK(...) before calling secret-keyed endpoints.", wantsPublishable ? "missing_publishable_key" : "missing_api_key", 0, null);
|
|
89
|
+
bearer = keyToUse;
|
|
90
|
+
}
|
|
91
|
+
if (headers instanceof axios.AxiosHeaders) headers.set("Authorization", `Bearer ${bearer}`);
|
|
92
|
+
else if (headers) headers["Authorization"] = `Bearer ${bearer}`;
|
|
82
93
|
return requestConfig;
|
|
83
94
|
});
|
|
84
95
|
client.interceptors.response.use((response) => response, (error) => {
|
|
@@ -92,33 +103,18 @@ const createApiClient = (config) => {
|
|
|
92
103
|
});
|
|
93
104
|
return client;
|
|
94
105
|
};
|
|
106
|
+
function buildRequestConfig(publishableDefault, opts) {
|
|
107
|
+
const { usePublishableKey = publishableDefault, authToken, ...rest } = opts ?? {};
|
|
108
|
+
const cfg = { ...rest };
|
|
109
|
+
if (usePublishableKey) cfg[FRAME_USE_PUBLISHABLE_KEY] = true;
|
|
110
|
+
if (authToken !== void 0) cfg[FRAME_AUTH_TOKEN] = authToken;
|
|
111
|
+
return cfg;
|
|
112
|
+
}
|
|
95
113
|
function withPublishableKey(opts) {
|
|
96
|
-
|
|
97
|
-
if (!usePublishableKey) return headers ? {
|
|
98
|
-
...rest,
|
|
99
|
-
headers
|
|
100
|
-
} : { ...rest };
|
|
101
|
-
return {
|
|
102
|
-
...rest,
|
|
103
|
-
headers: {
|
|
104
|
-
...headers ?? {},
|
|
105
|
-
[PUBLISHABLE_FLAG_KEY]: "1"
|
|
106
|
-
}
|
|
107
|
-
};
|
|
114
|
+
return buildRequestConfig(true, opts);
|
|
108
115
|
}
|
|
109
116
|
function maybePublishableKey(opts) {
|
|
110
|
-
|
|
111
|
-
if (!usePublishableKey) return headers ? {
|
|
112
|
-
...rest,
|
|
113
|
-
headers
|
|
114
|
-
} : { ...rest };
|
|
115
|
-
return {
|
|
116
|
-
...rest,
|
|
117
|
-
headers: {
|
|
118
|
-
...headers ?? {},
|
|
119
|
-
[PUBLISHABLE_FLAG_KEY]: "1"
|
|
120
|
-
}
|
|
121
|
-
};
|
|
117
|
+
return buildRequestConfig(false, opts);
|
|
122
118
|
}
|
|
123
119
|
//#endregion
|
|
124
120
|
//#region src/utils/paginator.ts
|
|
@@ -186,11 +182,11 @@ var CapabilitiesAPI = class {
|
|
|
186
182
|
constructor(client) {
|
|
187
183
|
this.client = client;
|
|
188
184
|
}
|
|
189
|
-
async list(accountId) {
|
|
190
|
-
return (await this.client.get(`/v1/accounts/${accountId}/capabilities
|
|
185
|
+
async list(accountId, opts) {
|
|
186
|
+
return (await this.client.get(`/v1/accounts/${accountId}/capabilities`, maybePublishableKey(opts))).data;
|
|
191
187
|
}
|
|
192
|
-
async request(accountId, params) {
|
|
193
|
-
return (await this.client.post(`/v1/accounts/${accountId}/capabilities`, params)).data;
|
|
188
|
+
async request(accountId, params, opts) {
|
|
189
|
+
return (await this.client.post(`/v1/accounts/${accountId}/capabilities`, params, maybePublishableKey(opts))).data;
|
|
194
190
|
}
|
|
195
191
|
async get(accountId, name) {
|
|
196
192
|
return (await this.client.get(`/v1/accounts/${accountId}/capabilities/${name}`)).data;
|
|
@@ -205,11 +201,14 @@ var OnboardingSessionsAPI = class {
|
|
|
205
201
|
constructor(client) {
|
|
206
202
|
this.client = client;
|
|
207
203
|
}
|
|
208
|
-
async create(params) {
|
|
209
|
-
return (await this.client.post("/v1/onboarding_sessions", params)).data;
|
|
204
|
+
async create(params, opts) {
|
|
205
|
+
return (await this.client.post("/v1/onboarding_sessions", params, maybePublishableKey(opts))).data;
|
|
210
206
|
}
|
|
211
|
-
async getByAccount(accountId) {
|
|
212
|
-
return (await this.client.get("/v1/onboarding_sessions", {
|
|
207
|
+
async getByAccount(accountId, opts) {
|
|
208
|
+
return (await this.client.get("/v1/onboarding_sessions", {
|
|
209
|
+
...maybePublishableKey(opts),
|
|
210
|
+
params: { account_id: accountId }
|
|
211
|
+
})).data;
|
|
213
212
|
}
|
|
214
213
|
};
|
|
215
214
|
//#endregion
|
|
@@ -338,8 +337,8 @@ var ChargeIntentsAPI = class {
|
|
|
338
337
|
async update(id, params) {
|
|
339
338
|
return (await this.client.patch(`/v1/charge_intents/${id}`, params)).data;
|
|
340
339
|
}
|
|
341
|
-
async get(id) {
|
|
342
|
-
return (await this.client.get(`/v1/charge_intents/${id}
|
|
340
|
+
async get(id, opts) {
|
|
341
|
+
return (await this.client.get(`/v1/charge_intents/${id}`, maybePublishableKey(opts))).data;
|
|
343
342
|
}
|
|
344
343
|
async list(per_page, page) {
|
|
345
344
|
return (await this.client.get("/v1/charge_intents", { params: {
|
|
@@ -996,14 +995,14 @@ var ThreeDSAPI = class {
|
|
|
996
995
|
constructor(client) {
|
|
997
996
|
this.client = client;
|
|
998
997
|
}
|
|
999
|
-
async create(params) {
|
|
1000
|
-
return (await this.client.post("/v1/3ds/intents", params)).data;
|
|
998
|
+
async create(params, opts) {
|
|
999
|
+
return (await this.client.post("/v1/3ds/intents", params, maybePublishableKey(opts))).data;
|
|
1001
1000
|
}
|
|
1002
|
-
async get(id) {
|
|
1003
|
-
return (await this.client.get(`/v1/3ds/intents/${id}
|
|
1001
|
+
async get(id, opts) {
|
|
1002
|
+
return (await this.client.get(`/v1/3ds/intents/${id}`, maybePublishableKey(opts))).data;
|
|
1004
1003
|
}
|
|
1005
|
-
async resend(id) {
|
|
1006
|
-
return (await this.client.post(`/v1/3ds/intents/${id}/resend
|
|
1004
|
+
async resend(id, opts) {
|
|
1005
|
+
return (await this.client.post(`/v1/3ds/intents/${id}/resend`, void 0, maybePublishableKey(opts))).data;
|
|
1007
1006
|
}
|
|
1008
1007
|
};
|
|
1009
1008
|
//#endregion
|
|
@@ -1048,8 +1047,8 @@ var TermsOfServiceAPI = class {
|
|
|
1048
1047
|
constructor(client) {
|
|
1049
1048
|
this.client = client;
|
|
1050
1049
|
}
|
|
1051
|
-
async createToken() {
|
|
1052
|
-
return (await this.client.post("/v1/terms_of_service", {})).data;
|
|
1050
|
+
async createToken(opts) {
|
|
1051
|
+
return (await this.client.post("/v1/terms_of_service", {}, maybePublishableKey(opts))).data;
|
|
1053
1052
|
}
|
|
1054
1053
|
async update(params) {
|
|
1055
1054
|
return (await this.client.patch("/v1/terms_of_service", params)).data;
|
|
@@ -1127,7 +1126,8 @@ var GeoComplianceAPI = class {
|
|
|
1127
1126
|
//#region src/index.ts
|
|
1128
1127
|
var FrameSDK = class {
|
|
1129
1128
|
constructor(config) {
|
|
1130
|
-
|
|
1129
|
+
this.onboardingSessionStore = createOnboardingSessionStore();
|
|
1130
|
+
const client = createApiClient(config, this.onboardingSessionStore);
|
|
1131
1131
|
this.accounts = new AccountsAPI(client);
|
|
1132
1132
|
this.capabilities = new CapabilitiesAPI(client);
|
|
1133
1133
|
this.onboardingSessions = new OnboardingSessionsAPI(client);
|
|
@@ -1167,6 +1167,33 @@ var FrameSDK = class {
|
|
|
1167
1167
|
this.wallet = new WalletAPI(client);
|
|
1168
1168
|
this.geoCompliance = new GeoComplianceAPI(client);
|
|
1169
1169
|
}
|
|
1170
|
+
/**
|
|
1171
|
+
* Begin an onboarding session. While a session is active, every request sends
|
|
1172
|
+
* `Authorization: Bearer <token>` (e.g. an `onb_sess_...` token), overriding
|
|
1173
|
+
* the configured publishable/secret keys regardless of `usePublishableKey`.
|
|
1174
|
+
* A per-request `authToken` (object client_secret) still takes precedence.
|
|
1175
|
+
*
|
|
1176
|
+
* Mirrors the native iOS `beginOnboardingSession`.
|
|
1177
|
+
*/
|
|
1178
|
+
setOnboardingSession(token) {
|
|
1179
|
+
this.onboardingSessionStore.token = token;
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* End the active onboarding session, reverting auth to the configured
|
|
1183
|
+
* publishable/secret keys.
|
|
1184
|
+
*
|
|
1185
|
+
* Safe-clear: when `token` is provided, the session is cleared only if it
|
|
1186
|
+
* matches the currently active token. This mirrors Android's guarded
|
|
1187
|
+
* teardown so a stale unmount cannot wipe a newer session. Omit `token` to
|
|
1188
|
+
* force-clear unconditionally.
|
|
1189
|
+
*
|
|
1190
|
+
* @returns true if a session was cleared, false if the guard prevented it.
|
|
1191
|
+
*/
|
|
1192
|
+
clearOnboardingSession(token) {
|
|
1193
|
+
if (token !== void 0 && this.onboardingSessionStore.token !== token) return false;
|
|
1194
|
+
this.onboardingSessionStore.token = null;
|
|
1195
|
+
return true;
|
|
1196
|
+
}
|
|
1170
1197
|
};
|
|
1171
1198
|
//#endregion
|
|
1172
1199
|
exports.FrameAPIError = FrameAPIError;
|