@flopay/js 1.1.0 → 1.1.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/README.md +21 -0
- package/dist/index.cjs +123 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -70
- package/dist/index.d.ts +28 -70
- package/dist/index.mjs +130 -70
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -167,30 +167,14 @@ declare class StripeAdapter implements PaymentProviderAdapter {
|
|
|
167
167
|
* Values from the server response always win — cached values fill in only
|
|
168
168
|
* where the server returned `null` or `undefined`.
|
|
169
169
|
*/
|
|
170
|
-
/** Display-only fields per
|
|
171
|
-
interface
|
|
172
|
-
/** Catalog code (
|
|
170
|
+
/** Display-only fields per product that can be cached and merged back later. */
|
|
171
|
+
interface SessionDisplayProduct {
|
|
172
|
+
/** Catalog code (match key). */
|
|
173
173
|
code?: string;
|
|
174
|
-
/**
|
|
175
|
-
|
|
176
|
-
/** Display-only name for the
|
|
177
|
-
|
|
178
|
-
/** @deprecated Use `itemName`. */
|
|
179
|
-
providerItemName?: string | null;
|
|
180
|
-
totalAmount?: number;
|
|
181
|
-
overrideAmount?: number | null;
|
|
182
|
-
currency?: string;
|
|
183
|
-
}
|
|
184
|
-
/** Display-only fields per subscription that can be cached and merged back later. */
|
|
185
|
-
interface SessionDisplaySubscription {
|
|
186
|
-
/** Catalog code (preferred match key). */
|
|
187
|
-
code?: string;
|
|
188
|
-
/** @deprecated Match key fallback when `code` is not provided. */
|
|
189
|
-
providerPlanId?: string;
|
|
190
|
-
/** Display-only name for the subscription plan. Takes priority over `providerPlanName`. */
|
|
191
|
-
subscriptionName?: string | null;
|
|
192
|
-
/** @deprecated Use `subscriptionName`. */
|
|
193
|
-
providerPlanName?: string | null;
|
|
174
|
+
/** Whether this product is a one-time item or a recurring subscription. */
|
|
175
|
+
type?: 'item' | 'subscription';
|
|
176
|
+
/** Display-only name for the product. */
|
|
177
|
+
name?: string | null;
|
|
194
178
|
totalAmount?: number;
|
|
195
179
|
overrideAmount?: number | null;
|
|
196
180
|
currency?: string;
|
|
@@ -199,8 +183,7 @@ interface SessionDisplaySubscription {
|
|
|
199
183
|
interface SessionDisplayCacheData {
|
|
200
184
|
/** Session-level currency (falls into the response only when the server omits it). */
|
|
201
185
|
currency?: string;
|
|
202
|
-
|
|
203
|
-
subscriptions?: SessionDisplaySubscription[];
|
|
186
|
+
products?: SessionDisplayProduct[];
|
|
204
187
|
}
|
|
205
188
|
/**
|
|
206
189
|
* Stash display-only data for a session. Called client-side right after the
|
|
@@ -232,57 +215,35 @@ interface RawCheckoutSession {
|
|
|
232
215
|
status: 'pending' | 'completed' | 'expired';
|
|
233
216
|
successUrl: string;
|
|
234
217
|
cancelUrl: string;
|
|
235
|
-
/** Session-level currency.
|
|
218
|
+
/** Session-level currency. */
|
|
236
219
|
currency?: string;
|
|
237
220
|
createdAt?: string;
|
|
238
221
|
checkoutUrl?: string;
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
checkoutSessionId: string;
|
|
242
|
-
/** Preferred catalog code. Falls back to the deprecated `providerItemId`. */
|
|
243
|
-
code?: string;
|
|
244
|
-
/** @deprecated Use `code`. */
|
|
245
|
-
providerItemId?: string;
|
|
246
|
-
/** Display-only name. Preferred over `providerItemName`. */
|
|
247
|
-
itemName?: string | null;
|
|
248
|
-
/** @deprecated Use `itemName`. Mirrored for backward compatibility. */
|
|
249
|
-
providerItemName?: string | null;
|
|
250
|
-
/** Display-only description from the catalog. */
|
|
251
|
-
providerItemDescription?: string | null;
|
|
252
|
-
quantity: number;
|
|
253
|
-
/** @deprecated Removed from the backend — resolved from the catalog. */
|
|
254
|
-
totalAmount?: number;
|
|
255
|
-
/** @deprecated Removed from the backend entirely. */
|
|
256
|
-
overrideAmount?: number | null;
|
|
257
|
-
/** @deprecated Use the session-level `currency`. */
|
|
258
|
-
currency?: string;
|
|
259
|
-
metadata?: Record<string, unknown> | null;
|
|
260
|
-
}>;
|
|
261
|
-
subscriptions: Array<{
|
|
222
|
+
/** Unified products list returned by the billing API (post-#760). */
|
|
223
|
+
products?: Array<{
|
|
262
224
|
uuid: string;
|
|
263
225
|
checkoutSessionId: string;
|
|
264
|
-
/**
|
|
226
|
+
/** 'item' or 'subscription'. */
|
|
227
|
+
type: 'item' | 'subscription';
|
|
265
228
|
code?: string;
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
/** Display-only name. Preferred over `providerPlanName`. */
|
|
269
|
-
subscriptionName?: string | null;
|
|
270
|
-
/** @deprecated Use `subscriptionName`. Mirrored for backward compatibility. */
|
|
271
|
-
providerPlanName?: string | null;
|
|
272
|
-
/** Display-only description from the catalog. */
|
|
273
|
-
providerPlanDescription?: string | null;
|
|
229
|
+
name?: string | null;
|
|
230
|
+
description?: string | null;
|
|
274
231
|
quantity: number;
|
|
275
|
-
/** @deprecated Removed from the backend — resolved from the catalog. */
|
|
276
232
|
totalAmount?: number;
|
|
277
|
-
/** @deprecated Removed from the backend entirely. */
|
|
278
233
|
overrideAmount?: number | null;
|
|
279
|
-
/** @deprecated Use the session-level `currency`. */
|
|
280
234
|
currency?: string;
|
|
281
|
-
/** @deprecated Removed from the backend. Checkouts only create new subscriptions. */
|
|
282
|
-
isUpdate?: boolean;
|
|
283
235
|
metadata?: Record<string, unknown> | null;
|
|
284
236
|
}>;
|
|
285
237
|
coupons?: string[];
|
|
238
|
+
/**
|
|
239
|
+
* Pre-discount total in cart-currency major units. Populated by billing
|
|
240
|
+
* API ≥ v1.1.2; absent on older backends.
|
|
241
|
+
*/
|
|
242
|
+
subtotalAmount?: number;
|
|
243
|
+
/** Total reduction from applied coupons (cart-currency major units). */
|
|
244
|
+
discountAmount?: number;
|
|
245
|
+
/** Final charge amount after coupon discount (cart-currency major units). */
|
|
246
|
+
totalAmount?: number;
|
|
286
247
|
checkoutMode?: 'full' | 'auto' | 'confirm';
|
|
287
248
|
gateways?: CheckoutGateways;
|
|
288
249
|
accountData: {
|
|
@@ -421,18 +382,15 @@ declare class PaymentAPI {
|
|
|
421
382
|
* Stash the display-only fields the consumer passed into a create-session
|
|
422
383
|
* call. Runs after the backend assigns a UUID so a later GET on the same
|
|
423
384
|
* session (typically after a redirect) can fill in fields the backend no
|
|
424
|
-
* longer persists — `overrideAmount`, `totalAmount`, `
|
|
385
|
+
* longer persists — `overrideAmount`, `totalAmount`, `name`, etc.
|
|
425
386
|
*
|
|
426
387
|
* No-op when no UUID is available.
|
|
427
388
|
*/
|
|
428
389
|
private autoCacheDisplayData;
|
|
429
390
|
/**
|
|
430
391
|
* Merge cached display-only fields (set by {@link cacheSessionDisplayData})
|
|
431
|
-
* into a raw session response
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
* Server values always win — cache fills in only where the server returned
|
|
435
|
-
* `null` / `undefined`.
|
|
392
|
+
* into a raw session response. Server values always win — cache fills in
|
|
393
|
+
* only where the server returned `null` / `undefined`.
|
|
436
394
|
*/
|
|
437
395
|
private mergeCachedDisplayData;
|
|
438
396
|
}
|
|
@@ -478,4 +436,4 @@ declare function createCheckoutSessionWithRetries(options: CreateSessionParams &
|
|
|
478
436
|
maxRetries?: number;
|
|
479
437
|
}): Promise<CheckoutSessionResult>;
|
|
480
438
|
|
|
481
|
-
export { FloPay, FloPayElements, PaymentAPI, type SessionDisplayCacheData, type
|
|
439
|
+
export { FloPay, FloPayElements, PaymentAPI, type SessionDisplayCacheData, type SessionDisplayProduct, StripeAdapter, cacheSessionDisplayData, clearSessionDisplayData, createCheckoutSession, createCheckoutSessionWithRetries, getSessionDisplayData, loadFloPay };
|
package/dist/index.mjs
CHANGED
|
@@ -449,8 +449,9 @@ var FloPayElements = class {
|
|
|
449
449
|
// src/payment-api.ts
|
|
450
450
|
import {
|
|
451
451
|
FloPayError as FloPayError3,
|
|
452
|
-
|
|
453
|
-
|
|
452
|
+
SDK_VERSION,
|
|
453
|
+
buildProductPayload,
|
|
454
|
+
foldIntoProducts,
|
|
454
455
|
resolveSessionCurrency
|
|
455
456
|
} from "@flopay/shared";
|
|
456
457
|
|
|
@@ -704,19 +705,28 @@ var PaymentAPI = class {
|
|
|
704
705
|
* Falls back to create + GET if the backend doesn't support `expand`.
|
|
705
706
|
*/
|
|
706
707
|
async createAndFetchSession(params) {
|
|
708
|
+
const wireProducts = params.products ?? foldIntoProducts(params.items, params.subscriptions);
|
|
707
709
|
const sessionCurrency = resolveSessionCurrency(
|
|
708
710
|
params.currency,
|
|
709
711
|
params.items,
|
|
710
|
-
params.subscriptions
|
|
712
|
+
params.subscriptions,
|
|
713
|
+
wireProducts
|
|
711
714
|
);
|
|
715
|
+
if (!sessionCurrency) {
|
|
716
|
+
throw new FloPayError3(
|
|
717
|
+
"currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.",
|
|
718
|
+
"validation_error",
|
|
719
|
+
{ code: "CurrencyRequired", param: "currency" }
|
|
720
|
+
);
|
|
721
|
+
}
|
|
712
722
|
const payload = {
|
|
713
723
|
clientId: params.clientId,
|
|
724
|
+
checkoutVersion: SDK_VERSION,
|
|
714
725
|
successUrl: params.successUrl,
|
|
715
726
|
cancelUrl: params.cancelUrl,
|
|
716
727
|
currency: sessionCurrency,
|
|
717
728
|
checkoutMode: params.checkoutMode ?? "full",
|
|
718
|
-
|
|
719
|
-
subscriptions: (params.subscriptions ?? []).map((sub) => buildSubscriptionPayload(sub, sessionCurrency)),
|
|
729
|
+
products: wireProducts.map((product) => buildProductPayload(product, sessionCurrency)),
|
|
720
730
|
accountData: {
|
|
721
731
|
userId: params.account.userId,
|
|
722
732
|
firstName: params.account.firstName ?? null,
|
|
@@ -848,17 +858,20 @@ var PaymentAPI = class {
|
|
|
848
858
|
}
|
|
849
859
|
/** Convert raw session to the SDK CheckoutSession shape. */
|
|
850
860
|
toCheckoutSession(raw) {
|
|
851
|
-
const
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
861
|
+
const rawProducts = raw.products ?? [];
|
|
862
|
+
const hasBackendTotal = typeof raw.totalAmount === "number" && Number.isFinite(raw.totalAmount);
|
|
863
|
+
const computedTotal = rawProducts.reduce(
|
|
864
|
+
(sum, p) => sum + (p.overrideAmount ?? p.totalAmount ?? 0),
|
|
865
|
+
0
|
|
866
|
+
);
|
|
867
|
+
const totalAmount = hasBackendTotal ? raw.totalAmount : computedTotal;
|
|
855
868
|
const amountInCents = Math.round(totalAmount * 100);
|
|
856
|
-
const currency = raw.currency ??
|
|
869
|
+
const currency = raw.currency ?? rawProducts[0]?.currency ?? "USD";
|
|
870
|
+
const mode = rawProducts.some((p) => p.type === "subscription") ? "subscription" : "payment";
|
|
857
871
|
return {
|
|
858
|
-
// Core fields (backward compat)
|
|
859
872
|
id: raw.uuid,
|
|
860
873
|
clientSecret: raw.nonce,
|
|
861
|
-
mode
|
|
874
|
+
mode,
|
|
862
875
|
status: this.toCheckoutSessionStatus(raw.status),
|
|
863
876
|
amount: amountInCents,
|
|
864
877
|
currency,
|
|
@@ -876,13 +889,20 @@ var PaymentAPI = class {
|
|
|
876
889
|
line2: raw.accountData.addressLine2 ?? void 0
|
|
877
890
|
},
|
|
878
891
|
metadata: {},
|
|
879
|
-
// Full session data from billing API
|
|
880
892
|
checkoutMode: raw.checkoutMode,
|
|
881
|
-
|
|
882
|
-
|
|
893
|
+
products: rawProducts.map((p) => ({
|
|
894
|
+
...p,
|
|
895
|
+
totalAmount: typeof p.totalAmount === "number" ? p.totalAmount : void 0,
|
|
896
|
+
overrideAmount: typeof p.overrideAmount === "number" ? p.overrideAmount : null,
|
|
897
|
+
currency: typeof p.currency === "string" ? p.currency : void 0,
|
|
898
|
+
metadata: p.metadata ?? null
|
|
899
|
+
})),
|
|
883
900
|
successUrl: raw.successUrl,
|
|
884
901
|
cancelUrl: raw.cancelUrl,
|
|
885
902
|
coupons: raw.coupons,
|
|
903
|
+
subtotalAmount: raw.subtotalAmount,
|
|
904
|
+
discountAmount: raw.discountAmount,
|
|
905
|
+
totalAmount: raw.totalAmount,
|
|
886
906
|
createdAt: raw.createdAt,
|
|
887
907
|
gateways: raw.gateways,
|
|
888
908
|
accountData: raw.accountData,
|
|
@@ -941,70 +961,63 @@ var PaymentAPI = class {
|
|
|
941
961
|
* Stash the display-only fields the consumer passed into a create-session
|
|
942
962
|
* call. Runs after the backend assigns a UUID so a later GET on the same
|
|
943
963
|
* session (typically after a redirect) can fill in fields the backend no
|
|
944
|
-
* longer persists — `overrideAmount`, `totalAmount`, `
|
|
964
|
+
* longer persists — `overrideAmount`, `totalAmount`, `name`, etc.
|
|
945
965
|
*
|
|
946
966
|
* No-op when no UUID is available.
|
|
947
967
|
*/
|
|
948
968
|
autoCacheDisplayData(sessionId, params) {
|
|
949
969
|
if (!sessionId) return;
|
|
950
|
-
|
|
970
|
+
const products = params.products ?? foldIntoProducts(params.items, params.subscriptions);
|
|
971
|
+
if (products.length === 0 && !params.currency) {
|
|
951
972
|
return;
|
|
952
973
|
}
|
|
974
|
+
const usingUnifiedProducts = params.products !== void 0;
|
|
975
|
+
const sessionCurrency = resolveSessionCurrency(
|
|
976
|
+
params.currency,
|
|
977
|
+
usingUnifiedProducts ? void 0 : params.items,
|
|
978
|
+
usingUnifiedProducts ? void 0 : params.subscriptions,
|
|
979
|
+
products
|
|
980
|
+
);
|
|
953
981
|
cacheSessionDisplayData(sessionId, {
|
|
954
|
-
currency:
|
|
955
|
-
|
|
956
|
-
|
|
982
|
+
currency: sessionCurrency ?? void 0,
|
|
983
|
+
products: products.map((p) => ({
|
|
984
|
+
code: p.code ?? p.providerItemId ?? p.providerPlanId,
|
|
985
|
+
type: p.type,
|
|
986
|
+
name: p.name ?? p.itemName ?? p.providerItemName ?? p.subscriptionName ?? p.providerPlanName ?? null,
|
|
987
|
+
totalAmount: p.totalAmount,
|
|
988
|
+
overrideAmount: p.overrideAmount,
|
|
989
|
+
currency: p.currency ?? sessionCurrency ?? void 0
|
|
990
|
+
}))
|
|
957
991
|
});
|
|
958
992
|
}
|
|
959
993
|
/**
|
|
960
994
|
* Merge cached display-only fields (set by {@link cacheSessionDisplayData})
|
|
961
|
-
* into a raw session response
|
|
962
|
-
*
|
|
963
|
-
*
|
|
964
|
-
* Server values always win — cache fills in only where the server returned
|
|
965
|
-
* `null` / `undefined`.
|
|
995
|
+
* into a raw session response. Server values always win — cache fills in
|
|
996
|
+
* only where the server returned `null` / `undefined`.
|
|
966
997
|
*/
|
|
967
998
|
mergeCachedDisplayData(raw) {
|
|
968
999
|
const cached = getSessionDisplayData(raw.uuid);
|
|
969
|
-
const
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
const key =
|
|
977
|
-
|
|
978
|
-
|
|
1000
|
+
const cachedProducts = /* @__PURE__ */ new Map();
|
|
1001
|
+
const productKey = (type, code) => code && type ? `${type}:${code}` : void 0;
|
|
1002
|
+
for (const p of cached?.products ?? []) {
|
|
1003
|
+
const key = productKey(p.type, p.code);
|
|
1004
|
+
if (key) cachedProducts.set(key, p);
|
|
1005
|
+
}
|
|
1006
|
+
const mergedProducts = (raw.products ?? []).map((p) => {
|
|
1007
|
+
const key = productKey(p.type, p.code);
|
|
1008
|
+
const fallback = key ? cachedProducts.get(key) : void 0;
|
|
1009
|
+
return {
|
|
1010
|
+
...p,
|
|
1011
|
+
name: p.name ?? fallback?.name ?? null,
|
|
1012
|
+
totalAmount: p.totalAmount ?? fallback?.totalAmount,
|
|
1013
|
+
overrideAmount: p.overrideAmount ?? fallback?.overrideAmount,
|
|
1014
|
+
currency: p.currency ?? fallback?.currency
|
|
1015
|
+
};
|
|
1016
|
+
});
|
|
979
1017
|
return {
|
|
980
1018
|
...raw,
|
|
981
1019
|
currency: raw.currency ?? cached?.currency,
|
|
982
|
-
|
|
983
|
-
const key = item.code ?? item.providerItemId;
|
|
984
|
-
const fallback = key ? cachedItems.get(key) : void 0;
|
|
985
|
-
const resolvedName = item.itemName ?? item.providerItemName ?? fallback?.itemName ?? fallback?.providerItemName;
|
|
986
|
-
return {
|
|
987
|
-
...item,
|
|
988
|
-
itemName: resolvedName,
|
|
989
|
-
providerItemName: resolvedName,
|
|
990
|
-
totalAmount: item.totalAmount ?? fallback?.totalAmount,
|
|
991
|
-
overrideAmount: item.overrideAmount ?? fallback?.overrideAmount,
|
|
992
|
-
currency: item.currency ?? fallback?.currency
|
|
993
|
-
};
|
|
994
|
-
}),
|
|
995
|
-
subscriptions: raw.subscriptions.map((sub) => {
|
|
996
|
-
const key = sub.code ?? sub.providerPlanId;
|
|
997
|
-
const fallback = key ? cachedSubs.get(key) : void 0;
|
|
998
|
-
const resolvedName = sub.subscriptionName ?? sub.providerPlanName ?? fallback?.subscriptionName ?? fallback?.providerPlanName;
|
|
999
|
-
return {
|
|
1000
|
-
...sub,
|
|
1001
|
-
subscriptionName: resolvedName,
|
|
1002
|
-
providerPlanName: resolvedName,
|
|
1003
|
-
totalAmount: sub.totalAmount ?? fallback?.totalAmount,
|
|
1004
|
-
overrideAmount: sub.overrideAmount ?? fallback?.overrideAmount,
|
|
1005
|
-
currency: sub.currency ?? fallback?.currency
|
|
1006
|
-
};
|
|
1007
|
-
})
|
|
1020
|
+
products: mergedProducts
|
|
1008
1021
|
};
|
|
1009
1022
|
}
|
|
1010
1023
|
};
|
|
@@ -1141,16 +1154,39 @@ async function loadFloPay(publishableKey, options) {
|
|
|
1141
1154
|
|
|
1142
1155
|
// src/create-checkout-session.ts
|
|
1143
1156
|
import {
|
|
1144
|
-
|
|
1145
|
-
|
|
1157
|
+
FloPayError as FloPayError6,
|
|
1158
|
+
SDK_VERSION as SDK_VERSION2,
|
|
1159
|
+
buildProductPayload as buildProductPayload2,
|
|
1160
|
+
foldIntoProducts as foldIntoProducts2,
|
|
1146
1161
|
resolveSessionCurrency as resolveSessionCurrency2
|
|
1147
1162
|
} from "@flopay/shared";
|
|
1163
|
+
var MAX_COUPON_CODES = 5;
|
|
1164
|
+
function readString2(value) {
|
|
1165
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
1166
|
+
}
|
|
1167
|
+
function buildCheckoutSessionError(status, payload) {
|
|
1168
|
+
const nested = payload?.error;
|
|
1169
|
+
const code = readString2(payload?.code) ?? readString2(nested?.code) ?? `http_${status}`;
|
|
1170
|
+
const message = readString2(payload?.message) ?? readString2(nested?.message) ?? defaultMessageForCode(code, status);
|
|
1171
|
+
return new FloPayError6(message, "api_error", { code, statusCode: status });
|
|
1172
|
+
}
|
|
1173
|
+
function defaultMessageForCode(code, status) {
|
|
1174
|
+
switch (code) {
|
|
1175
|
+
case "CouponLimitExceeded":
|
|
1176
|
+
return `Too many coupon codes \u2014 a checkout session accepts at most ${MAX_COUPON_CODES}.`;
|
|
1177
|
+
case "CouponCurrencyUnsupported":
|
|
1178
|
+
return "One of the applied coupons has no price configured for the cart currency.";
|
|
1179
|
+
default:
|
|
1180
|
+
return `Failed to create checkout session (HTTP ${status}).`;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1148
1183
|
async function createCheckoutSession(options) {
|
|
1149
1184
|
const {
|
|
1150
1185
|
billingApiUrl,
|
|
1151
1186
|
checkoutBaseUrl,
|
|
1152
1187
|
items = [],
|
|
1153
1188
|
subscriptions = [],
|
|
1189
|
+
products,
|
|
1154
1190
|
account,
|
|
1155
1191
|
successUrl,
|
|
1156
1192
|
cancelUrl,
|
|
@@ -1164,15 +1200,30 @@ async function createCheckoutSession(options) {
|
|
|
1164
1200
|
currency,
|
|
1165
1201
|
utmMetadata
|
|
1166
1202
|
} = options;
|
|
1167
|
-
|
|
1203
|
+
if (couponCodes.length > MAX_COUPON_CODES) {
|
|
1204
|
+
throw new FloPayError6(
|
|
1205
|
+
`Too many coupon codes \u2014 a checkout session accepts at most ${MAX_COUPON_CODES}.`,
|
|
1206
|
+
"validation_error",
|
|
1207
|
+
{ code: "CouponLimitExceeded", param: "couponCodes" }
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
const wireProducts = products ?? foldIntoProducts2(items, subscriptions);
|
|
1211
|
+
const sessionCurrency = resolveSessionCurrency2(currency, items, subscriptions, wireProducts);
|
|
1212
|
+
if (!sessionCurrency) {
|
|
1213
|
+
throw new FloPayError6(
|
|
1214
|
+
"currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.",
|
|
1215
|
+
"validation_error",
|
|
1216
|
+
{ code: "CurrencyRequired", param: "currency" }
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1168
1219
|
const payload = {
|
|
1169
1220
|
clientId,
|
|
1221
|
+
checkoutVersion: SDK_VERSION2,
|
|
1170
1222
|
successUrl,
|
|
1171
1223
|
cancelUrl,
|
|
1172
1224
|
currency: sessionCurrency,
|
|
1173
1225
|
checkoutMode,
|
|
1174
|
-
|
|
1175
|
-
subscriptions: subscriptions.map((sub) => buildSubscriptionPayload2(sub, sessionCurrency)),
|
|
1226
|
+
products: wireProducts.map((product) => buildProductPayload2(product, sessionCurrency)),
|
|
1176
1227
|
accountData: {
|
|
1177
1228
|
userId: account.userId,
|
|
1178
1229
|
firstName: account.firstName ?? null,
|
|
@@ -1214,16 +1265,25 @@ async function createCheckoutSession(options) {
|
|
|
1214
1265
|
} finally {
|
|
1215
1266
|
clearTimeout(timer);
|
|
1216
1267
|
}
|
|
1268
|
+
if (status >= 400) {
|
|
1269
|
+
throw buildCheckoutSessionError(status, body);
|
|
1270
|
+
}
|
|
1217
1271
|
if (status === 201) {
|
|
1218
1272
|
const uuid = body?.data?.uuid;
|
|
1219
1273
|
if (!uuid) {
|
|
1220
1274
|
throw new Error("Checkout session created but no UUID was returned by the billing API");
|
|
1221
1275
|
}
|
|
1222
|
-
if (
|
|
1276
|
+
if (wireProducts.length || sessionCurrency) {
|
|
1223
1277
|
cacheSessionDisplayData(uuid, {
|
|
1224
|
-
currency,
|
|
1225
|
-
|
|
1226
|
-
|
|
1278
|
+
currency: sessionCurrency,
|
|
1279
|
+
products: wireProducts.map((p) => ({
|
|
1280
|
+
code: p.code ?? p.providerItemId ?? p.providerPlanId,
|
|
1281
|
+
type: p.type,
|
|
1282
|
+
name: p.name ?? p.itemName ?? p.providerItemName ?? p.subscriptionName ?? p.providerPlanName ?? null,
|
|
1283
|
+
totalAmount: p.totalAmount,
|
|
1284
|
+
overrideAmount: p.overrideAmount,
|
|
1285
|
+
currency: p.currency ?? sessionCurrency
|
|
1286
|
+
}))
|
|
1227
1287
|
});
|
|
1228
1288
|
}
|
|
1229
1289
|
const redirectUrl = new URL(`${checkoutBaseUrl.replace(/\/+$/, "")}/secure`);
|