@flopay/shared 1.0.3 → 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 +8 -4
- package/dist/index.cjs +103 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +217 -94
- package/dist/index.d.ts +217 -94
- package/dist/index.mjs +100 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,9 +79,13 @@ throw validationError('Email is required', 'email');
|
|
|
79
79
|
| `ButtonsLayoutStyles` | Style overrides for the `buttons` layout: `cardButton`, `cardButtonFontSize`, `cardFormContainer`, `cardInputBorder`, `cardInputColor`, `cardInputPlaceholderColor`, `cardInputFontSize`, `cardInputBackground`, `nameInput`, `backButton`, `backButtonFontSize`, `backButtonIcon`, `submitButton`, `submitButtonFontSize`, `title`, `titleFontSize`, `errorBanner` |
|
|
80
80
|
| `ButtonsLayoutTheme` | Theme preset name: `'default'` / `'minimal'` / `'rounded'` / `'dark'` |
|
|
81
81
|
| `CheckoutMode` | Checkout mode: `'full'` / `'auto'` / `'confirm'` |
|
|
82
|
-
| `CheckoutSession` | Session object: `id`, `clientSecret`, `mode`, `status`, `amount`, `currency`, `customer?`, `checkoutMode?`, `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
82
|
+
| `CheckoutSession` | Session object: `id`, `clientSecret`, `mode`, `status`, `amount`, `currency`, `customer?`, `checkoutMode?`, `products?` (unified `CheckoutSessionProduct[]`), `successUrl?`, `cancelUrl?`, `coupons?`, `subtotalAmount?` / `discountAmount?` / `totalAmount?` (cart-currency major units; populated by billing API ≥ v1.1.2), `gateways?` (map keyed by gateway code — `stripe`, `paypal`, …), `accountData?`, `tagsData?` |
|
|
83
|
+
| `CheckoutSessionProduct` | Unified product from session response: `uuid`, `checkoutSessionId`, `type` (`'item'`/`'subscription'`), `code?`, `name?`, `description?`, `quantity`, `totalAmount?`, `overrideAmount?`, `currency?`, `metadata?` |
|
|
84
|
+
| `CheckoutProductType` | `'item'` / `'subscription'` |
|
|
85
|
+
| `CheckoutProduct` | Unified product input for session creation: `type`, `code?`, `name?`, `quantity?`, `totalAmount?`, `overrideAmount?`, `currency?`, `metadata?` |
|
|
86
|
+
| `CheckoutGateway` | Per-gateway config: `publishableKey?`, `environment?` (`'sandbox'`/`'live'`), `stripeClientSecret?` (Stripe-only) |
|
|
87
|
+
| `CheckoutGateways` | Map keyed by gateway code (e.g. `stripe`, `paypal`). A session can advertise multiple concurrent gateways. |
|
|
88
|
+
| `GatewayEnvironment` | `'sandbox'` / `'live'` — drives the PayPal JS SDK script and any other environment-scoped behavior |
|
|
85
89
|
| `PaymentResult` | Payment outcome: `status` (`'succeeded'`/`'processing'`/`'requires_action'`/`'failed'`), `paymentIntentId?`, `error?` |
|
|
86
90
|
| `ConfirmPaymentParams` | `clientSecret`, `returnUrl?` |
|
|
87
91
|
| `CreatePaymentMethodResult` | `paymentMethodId`, `error?` |
|
|
@@ -100,7 +104,7 @@ throw validationError('Email is required', 'email');
|
|
|
100
104
|
| `TokenizedBody` | `id?`, `type?`, `threeDSecureActionResultTokenId?`, `originalPaymentMethodId?`, `isPaypal?` |
|
|
101
105
|
| `NormalizedCheckoutSession` | Provider-agnostic session: `provider`, `mode`, `data`, `raw?` |
|
|
102
106
|
| `CheckoutModeKind` | `'tokenize'` / `'redirect'` |
|
|
103
|
-
| `CreateSessionParams` | Full session creation params: `billingApiUrl`, `checkoutBaseUrl`, `clientId`, `items?`, `subscriptions
|
|
107
|
+
| `CreateSessionParams` | Full session creation params: `billingApiUrl`, `checkoutBaseUrl`, `clientId`, `currency` (required — SDK throws `FloPayError({ type: 'validation_error', code: 'CurrencyRequired' })` before issuing the request when none can be resolved), `products?` (unified shape; takes precedence), `items?`, `subscriptions?` (legacy — folded into `products[]` before send), `account`, `successUrl`, `cancelUrl`, `checkoutMode?`, `couponCodes?`, `tagsData?`, `redirectParams?`, `setCookie?`, `timeoutMs?`, `utmMetadata?` |
|
|
104
108
|
| `CheckoutSessionResult` | `{ status: 201; redirectUrl }` / `{ status: 204 }` / `{ status: number }` |
|
|
105
109
|
| `CheckoutItem` | `providerItemId`, `providerItemName?`, `quantity?`, `totalAmount`, `overrideAmount?`, `currency?` |
|
|
106
110
|
| `CheckoutSubscription` | `providerPlanId`, `providerPlanName?`, `quantity?`, `totalAmount`, `overrideAmount?`, `currency?` |
|
package/dist/index.cjs
CHANGED
|
@@ -45,8 +45,10 @@ __export(index_exports, {
|
|
|
45
45
|
authenticationError: () => authenticationError,
|
|
46
46
|
buildCheckoutDisplayData: () => buildCheckoutDisplayData,
|
|
47
47
|
buildItemPayload: () => buildItemPayload,
|
|
48
|
+
buildProductPayload: () => buildProductPayload,
|
|
48
49
|
buildSubscriptionPayload: () => buildSubscriptionPayload,
|
|
49
50
|
configureFlopay: () => configureFlopay,
|
|
51
|
+
foldIntoProducts: () => foldIntoProducts,
|
|
50
52
|
getConfiguredBillingApiUrl: () => getConfiguredBillingApiUrl,
|
|
51
53
|
getCountryByCode: () => getCountryByCode,
|
|
52
54
|
getCurrencyByCountry: () => getCurrencyByCountry,
|
|
@@ -60,6 +62,7 @@ __export(index_exports, {
|
|
|
60
62
|
isValidPublishableKey: () => isValidPublishableKey,
|
|
61
63
|
isValidSecretKey: () => isValidSecretKey,
|
|
62
64
|
networkError: () => networkError,
|
|
65
|
+
normalizeGatewayEnvironment: () => normalizeGatewayEnvironment,
|
|
63
66
|
rateLimitError: () => rateLimitError,
|
|
64
67
|
resolveAVSConfig: () => resolveAVSConfig,
|
|
65
68
|
resolveBillingApiUrl: () => resolveBillingApiUrl,
|
|
@@ -69,6 +72,24 @@ __export(index_exports, {
|
|
|
69
72
|
});
|
|
70
73
|
module.exports = __toCommonJS(index_exports);
|
|
71
74
|
|
|
75
|
+
// src/types.ts
|
|
76
|
+
function normalizeGatewayEnvironment(env) {
|
|
77
|
+
if (env == null) return void 0;
|
|
78
|
+
switch (env) {
|
|
79
|
+
case "sandbox":
|
|
80
|
+
case "stage":
|
|
81
|
+
return "sandbox";
|
|
82
|
+
case "live":
|
|
83
|
+
case "production":
|
|
84
|
+
return "live";
|
|
85
|
+
default:
|
|
86
|
+
console.warn(
|
|
87
|
+
`[FloPay] Unrecognized gateway environment "${env}". Expected one of: sandbox, live, stage, production. Falling back to undefined.`
|
|
88
|
+
);
|
|
89
|
+
return void 0;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
72
93
|
// src/errors.ts
|
|
73
94
|
var FloPayError = class extends Error {
|
|
74
95
|
constructor(message, type, options) {
|
|
@@ -116,7 +137,7 @@ function getFloPayEnvironment() {
|
|
|
116
137
|
}
|
|
117
138
|
|
|
118
139
|
// src/constants.ts
|
|
119
|
-
var SDK_VERSION = "1.
|
|
140
|
+
var SDK_VERSION = "1.1.3";
|
|
120
141
|
var BILLING_API_URL_STAGING = "https://api.stage.flopay.com";
|
|
121
142
|
var BILLING_API_URL_PRODUCTION = "https://api.flopay.com";
|
|
122
143
|
var DEFAULT_API_BASE_URL = BILLING_API_URL_STAGING;
|
|
@@ -775,7 +796,7 @@ function nonBlank(value) {
|
|
|
775
796
|
const trimmed = value.trim();
|
|
776
797
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
777
798
|
}
|
|
778
|
-
function resolveSessionCurrency(sessionCurrency, items, subscriptions) {
|
|
799
|
+
function resolveSessionCurrency(sessionCurrency, items, subscriptions, products) {
|
|
779
800
|
const session = nonBlank(sessionCurrency);
|
|
780
801
|
if (session) return session;
|
|
781
802
|
for (const item of items ?? []) {
|
|
@@ -786,7 +807,67 @@ function resolveSessionCurrency(sessionCurrency, items, subscriptions) {
|
|
|
786
807
|
const c = nonBlank(sub.currency);
|
|
787
808
|
if (c) return c;
|
|
788
809
|
}
|
|
789
|
-
|
|
810
|
+
for (const product of products ?? []) {
|
|
811
|
+
const c = nonBlank(product.currency);
|
|
812
|
+
if (c) return c;
|
|
813
|
+
}
|
|
814
|
+
return null;
|
|
815
|
+
}
|
|
816
|
+
function foldIntoProducts(items, subscriptions) {
|
|
817
|
+
const products = [];
|
|
818
|
+
for (const sub of subscriptions ?? []) {
|
|
819
|
+
products.push({
|
|
820
|
+
type: "subscription",
|
|
821
|
+
code: nonBlank(sub.code) ?? nonBlank(sub.providerPlanId),
|
|
822
|
+
providerPlanId: sub.providerPlanId,
|
|
823
|
+
name: nonBlank(sub.subscriptionName) ?? nonBlank(sub.providerPlanName) ?? null,
|
|
824
|
+
subscriptionName: sub.subscriptionName ?? null,
|
|
825
|
+
providerPlanName: sub.providerPlanName ?? null,
|
|
826
|
+
quantity: sub.quantity,
|
|
827
|
+
totalAmount: sub.totalAmount,
|
|
828
|
+
overrideAmount: sub.overrideAmount,
|
|
829
|
+
currency: sub.currency,
|
|
830
|
+
metadata: sub.metadata
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
for (const item of items ?? []) {
|
|
834
|
+
products.push({
|
|
835
|
+
type: "item",
|
|
836
|
+
code: nonBlank(item.code) ?? nonBlank(item.providerItemId),
|
|
837
|
+
providerItemId: item.providerItemId,
|
|
838
|
+
name: nonBlank(item.itemName) ?? nonBlank(item.providerItemName) ?? null,
|
|
839
|
+
itemName: item.itemName ?? null,
|
|
840
|
+
providerItemName: item.providerItemName ?? null,
|
|
841
|
+
quantity: item.quantity,
|
|
842
|
+
totalAmount: item.totalAmount,
|
|
843
|
+
overrideAmount: item.overrideAmount,
|
|
844
|
+
currency: item.currency,
|
|
845
|
+
metadata: item.metadata
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
return products;
|
|
849
|
+
}
|
|
850
|
+
function buildProductPayload(product, sessionCurrency) {
|
|
851
|
+
const code = nonBlank(product.code) ?? nonBlank(product.providerItemId) ?? nonBlank(product.providerPlanId);
|
|
852
|
+
if (!code) {
|
|
853
|
+
throw new Error(
|
|
854
|
+
"CheckoutProduct requires `code` (or the deprecated `providerItemId` / `providerPlanId`)."
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
const name = nonBlank(product.name) ?? nonBlank(product.itemName) ?? nonBlank(product.providerItemName) ?? nonBlank(product.subscriptionName) ?? nonBlank(product.providerPlanName) ?? null;
|
|
858
|
+
const payload = {
|
|
859
|
+
type: product.type,
|
|
860
|
+
code,
|
|
861
|
+
name,
|
|
862
|
+
quantity: product.quantity ?? 1,
|
|
863
|
+
totalAmount: product.totalAmount,
|
|
864
|
+
overrideAmount: product.overrideAmount ?? null,
|
|
865
|
+
currency: nonBlank(product.currency) ?? sessionCurrency
|
|
866
|
+
};
|
|
867
|
+
if (product.metadata) {
|
|
868
|
+
payload["metadata"] = product.metadata;
|
|
869
|
+
}
|
|
870
|
+
return payload;
|
|
790
871
|
}
|
|
791
872
|
function buildItemPayload(item, sessionCurrency) {
|
|
792
873
|
const code = nonBlank(item.code) ?? nonBlank(item.providerItemId);
|
|
@@ -836,13 +917,15 @@ function buildSubscriptionPayload(subscription, sessionCurrency) {
|
|
|
836
917
|
// src/display.ts
|
|
837
918
|
function buildCheckoutDisplayData(session, options = {}) {
|
|
838
919
|
const itemsList = [];
|
|
839
|
-
const
|
|
840
|
-
const
|
|
841
|
-
const
|
|
920
|
+
const products = session.products ?? [];
|
|
921
|
+
const subscriptions = products.filter((p) => p.type === "subscription");
|
|
922
|
+
const items = products.filter((p) => p.type === "item");
|
|
923
|
+
const resolvedCurrency = resolveSessionCurrency(session.currency, void 0, void 0, products);
|
|
924
|
+
const currency = (resolvedCurrency ?? "USD").toUpperCase();
|
|
842
925
|
for (const sub of subscriptions) {
|
|
843
926
|
const originalPrice = sub.totalAmount ?? 0;
|
|
844
927
|
const discountedPrice = sub.overrideAmount ?? originalPrice;
|
|
845
|
-
let name = sub.
|
|
928
|
+
let name = sub.name || sub.code || "Subscription";
|
|
846
929
|
if (name === "4-WEEK PLAN" && discountedPrice <= 1) {
|
|
847
930
|
name = "7-DAY TRIAL: FULL ACCESS";
|
|
848
931
|
}
|
|
@@ -859,7 +942,7 @@ function buildCheckoutDisplayData(session, options = {}) {
|
|
|
859
942
|
const originalPrice = item.totalAmount ?? 0;
|
|
860
943
|
const discountedPrice = item.overrideAmount ?? originalPrice;
|
|
861
944
|
itemsList.push({
|
|
862
|
-
name: item.
|
|
945
|
+
name: item.name || item.code || "Item",
|
|
863
946
|
quantity: item.quantity,
|
|
864
947
|
price: discountedPrice,
|
|
865
948
|
originalPrice
|
|
@@ -874,8 +957,13 @@ function buildCheckoutDisplayData(session, options = {}) {
|
|
|
874
957
|
originalPrice: session.amount / 100
|
|
875
958
|
});
|
|
876
959
|
}
|
|
877
|
-
const
|
|
878
|
-
const
|
|
960
|
+
const lineOriginalTotal = itemsList.reduce((sum, i) => sum + i.originalPrice * i.quantity, 0);
|
|
961
|
+
const lineTotal = itemsList.reduce((sum, i) => sum + i.price * i.quantity, 0);
|
|
962
|
+
const hasBackendTotals = typeof session.totalAmount === "number" && Number.isFinite(session.totalAmount);
|
|
963
|
+
const hasBackendSubtotal = typeof session.subtotalAmount === "number" && Number.isFinite(session.subtotalAmount);
|
|
964
|
+
const hasBackendDiscount = typeof session.discountAmount === "number" && Number.isFinite(session.discountAmount);
|
|
965
|
+
const total = hasBackendTotals ? session.totalAmount : lineTotal;
|
|
966
|
+
const originalTotal = hasBackendSubtotal ? session.subtotalAmount : lineOriginalTotal;
|
|
879
967
|
const totalSave = Math.max(0, originalTotal - total);
|
|
880
968
|
const discountPercent = originalTotal > 0 ? Math.round(totalSave / originalTotal * 100) : 0;
|
|
881
969
|
return {
|
|
@@ -884,7 +972,8 @@ function buildCheckoutDisplayData(session, options = {}) {
|
|
|
884
972
|
total,
|
|
885
973
|
originalTotal,
|
|
886
974
|
totalSave,
|
|
887
|
-
discountPercent
|
|
975
|
+
discountPercent,
|
|
976
|
+
...hasBackendDiscount ? { couponDiscount: session.discountAmount } : {}
|
|
888
977
|
};
|
|
889
978
|
}
|
|
890
979
|
|
|
@@ -925,8 +1014,10 @@ function isValidSecretKey(key) {
|
|
|
925
1014
|
authenticationError,
|
|
926
1015
|
buildCheckoutDisplayData,
|
|
927
1016
|
buildItemPayload,
|
|
1017
|
+
buildProductPayload,
|
|
928
1018
|
buildSubscriptionPayload,
|
|
929
1019
|
configureFlopay,
|
|
1020
|
+
foldIntoProducts,
|
|
930
1021
|
getConfiguredBillingApiUrl,
|
|
931
1022
|
getCountryByCode,
|
|
932
1023
|
getCurrencyByCountry,
|
|
@@ -940,6 +1031,7 @@ function isValidSecretKey(key) {
|
|
|
940
1031
|
isValidPublishableKey,
|
|
941
1032
|
isValidSecretKey,
|
|
942
1033
|
networkError,
|
|
1034
|
+
normalizeGatewayEnvironment,
|
|
943
1035
|
rateLimitError,
|
|
944
1036
|
resolveAVSConfig,
|
|
945
1037
|
resolveBillingApiUrl,
|