@flopay/shared 1.1.0 → 1.1.4
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 +5 -4
- package/dist/index.cjs +83 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +141 -71
- package/dist/index.d.ts +141 -71
- package/dist/index.mjs +81 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,12 +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?`, `
|
|
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?` |
|
|
83
86
|
| `CheckoutGateway` | Per-gateway config: `publishableKey?`, `environment?` (`'sandbox'`/`'live'`), `stripeClientSecret?` (Stripe-only) |
|
|
84
87
|
| `CheckoutGateways` | Map keyed by gateway code (e.g. `stripe`, `paypal`). A session can advertise multiple concurrent gateways. |
|
|
85
88
|
| `GatewayEnvironment` | `'sandbox'` / `'live'` — drives the PayPal JS SDK script and any other environment-scoped behavior |
|
|
86
|
-
| `CheckoutSessionItem` | Session item from billing API: `uuid`, `providerItemId`, `providerItemName`, `quantity`, `totalAmount`, `overrideAmount`, `currency` |
|
|
87
|
-
| `CheckoutSessionSubscription` | Session subscription from billing API: `uuid`, `providerPlanId`, `providerPlanName`, `quantity`, `totalAmount`, `overrideAmount`, `currency` |
|
|
88
89
|
| `PaymentResult` | Payment outcome: `status` (`'succeeded'`/`'processing'`/`'requires_action'`/`'failed'`), `paymentIntentId?`, `error?` |
|
|
89
90
|
| `ConfirmPaymentParams` | `clientSecret`, `returnUrl?` |
|
|
90
91
|
| `CreatePaymentMethodResult` | `paymentMethodId`, `error?` |
|
|
@@ -103,7 +104,7 @@ throw validationError('Email is required', 'email');
|
|
|
103
104
|
| `TokenizedBody` | `id?`, `type?`, `threeDSecureActionResultTokenId?`, `originalPaymentMethodId?`, `isPaypal?` |
|
|
104
105
|
| `NormalizedCheckoutSession` | Provider-agnostic session: `provider`, `mode`, `data`, `raw?` |
|
|
105
106
|
| `CheckoutModeKind` | `'tokenize'` / `'redirect'` |
|
|
106
|
-
| `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?` |
|
|
107
108
|
| `CheckoutSessionResult` | `{ status: 201; redirectUrl }` / `{ status: 204 }` / `{ status: number }` |
|
|
108
109
|
| `CheckoutItem` | `providerItemId`, `providerItemName?`, `quantity?`, `totalAmount`, `overrideAmount?`, `currency?` |
|
|
109
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,
|
|
@@ -135,7 +137,7 @@ function getFloPayEnvironment() {
|
|
|
135
137
|
}
|
|
136
138
|
|
|
137
139
|
// src/constants.ts
|
|
138
|
-
var SDK_VERSION = "1.1.
|
|
140
|
+
var SDK_VERSION = "1.1.4";
|
|
139
141
|
var BILLING_API_URL_STAGING = "https://api.stage.flopay.com";
|
|
140
142
|
var BILLING_API_URL_PRODUCTION = "https://api.flopay.com";
|
|
141
143
|
var DEFAULT_API_BASE_URL = BILLING_API_URL_STAGING;
|
|
@@ -794,7 +796,7 @@ function nonBlank(value) {
|
|
|
794
796
|
const trimmed = value.trim();
|
|
795
797
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
796
798
|
}
|
|
797
|
-
function resolveSessionCurrency(sessionCurrency, items, subscriptions) {
|
|
799
|
+
function resolveSessionCurrency(sessionCurrency, items, subscriptions, products) {
|
|
798
800
|
const session = nonBlank(sessionCurrency);
|
|
799
801
|
if (session) return session;
|
|
800
802
|
for (const item of items ?? []) {
|
|
@@ -805,7 +807,67 @@ function resolveSessionCurrency(sessionCurrency, items, subscriptions) {
|
|
|
805
807
|
const c = nonBlank(sub.currency);
|
|
806
808
|
if (c) return c;
|
|
807
809
|
}
|
|
808
|
-
|
|
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;
|
|
809
871
|
}
|
|
810
872
|
function buildItemPayload(item, sessionCurrency) {
|
|
811
873
|
const code = nonBlank(item.code) ?? nonBlank(item.providerItemId);
|
|
@@ -855,13 +917,15 @@ function buildSubscriptionPayload(subscription, sessionCurrency) {
|
|
|
855
917
|
// src/display.ts
|
|
856
918
|
function buildCheckoutDisplayData(session, options = {}) {
|
|
857
919
|
const itemsList = [];
|
|
858
|
-
const
|
|
859
|
-
const
|
|
860
|
-
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();
|
|
861
925
|
for (const sub of subscriptions) {
|
|
862
926
|
const originalPrice = sub.totalAmount ?? 0;
|
|
863
927
|
const discountedPrice = sub.overrideAmount ?? originalPrice;
|
|
864
|
-
let name = sub.
|
|
928
|
+
let name = sub.name || sub.code || "Subscription";
|
|
865
929
|
if (name === "4-WEEK PLAN" && discountedPrice <= 1) {
|
|
866
930
|
name = "7-DAY TRIAL: FULL ACCESS";
|
|
867
931
|
}
|
|
@@ -878,7 +942,7 @@ function buildCheckoutDisplayData(session, options = {}) {
|
|
|
878
942
|
const originalPrice = item.totalAmount ?? 0;
|
|
879
943
|
const discountedPrice = item.overrideAmount ?? originalPrice;
|
|
880
944
|
itemsList.push({
|
|
881
|
-
name: item.
|
|
945
|
+
name: item.name || item.code || "Item",
|
|
882
946
|
quantity: item.quantity,
|
|
883
947
|
price: discountedPrice,
|
|
884
948
|
originalPrice
|
|
@@ -893,8 +957,13 @@ function buildCheckoutDisplayData(session, options = {}) {
|
|
|
893
957
|
originalPrice: session.amount / 100
|
|
894
958
|
});
|
|
895
959
|
}
|
|
896
|
-
const
|
|
897
|
-
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;
|
|
898
967
|
const totalSave = Math.max(0, originalTotal - total);
|
|
899
968
|
const discountPercent = originalTotal > 0 ? Math.round(totalSave / originalTotal * 100) : 0;
|
|
900
969
|
return {
|
|
@@ -903,7 +972,8 @@ function buildCheckoutDisplayData(session, options = {}) {
|
|
|
903
972
|
total,
|
|
904
973
|
originalTotal,
|
|
905
974
|
totalSave,
|
|
906
|
-
discountPercent
|
|
975
|
+
discountPercent,
|
|
976
|
+
...hasBackendDiscount ? { couponDiscount: session.discountAmount } : {}
|
|
907
977
|
};
|
|
908
978
|
}
|
|
909
979
|
|
|
@@ -944,8 +1014,10 @@ function isValidSecretKey(key) {
|
|
|
944
1014
|
authenticationError,
|
|
945
1015
|
buildCheckoutDisplayData,
|
|
946
1016
|
buildItemPayload,
|
|
1017
|
+
buildProductPayload,
|
|
947
1018
|
buildSubscriptionPayload,
|
|
948
1019
|
configureFlopay,
|
|
1020
|
+
foldIntoProducts,
|
|
949
1021
|
getConfiguredBillingApiUrl,
|
|
950
1022
|
getCountryByCode,
|
|
951
1023
|
getCurrencyByCountry,
|