@funnelfox/billing 0.9.0-beta.7 → 0.9.0-ffb-467-adyen.13
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 +211 -0
- package/dist/chunk-adyen-card-form.cjs.js +190 -0
- package/dist/chunk-adyen-card-form.es.js +188 -0
- package/dist/chunk-index.cjs.js +125 -10
- package/dist/chunk-index.es.js +124 -11
- package/dist/chunk-stripe-card-form.cjs.js +94 -10
- package/dist/chunk-stripe-card-form.es.js +94 -10
- package/dist/chunk-stripe-loader.es.js +1 -1
- package/dist/chunk-stripe-wallet.cjs.js +37 -7
- package/dist/chunk-stripe-wallet.es.js +37 -7
- package/dist/funnelfox-billing.esm.js +1 -1
- package/dist/funnelfox-billing.js +437 -27
- package/dist/funnelfox-billing.min.js +1 -1
- package/package.json +2 -2
- package/src/types.d.ts +84 -1
package/dist/chunk-index.cjs.js
CHANGED
|
@@ -491,7 +491,7 @@ exports.PaymentMethod = void 0;
|
|
|
491
491
|
/**
|
|
492
492
|
* @fileoverview Constants for Funnefox SDK
|
|
493
493
|
*/
|
|
494
|
-
const SDK_VERSION = '0.9.0-beta.
|
|
494
|
+
const SDK_VERSION = '0.9.0-beta.1';
|
|
495
495
|
const DEFAULTS = {
|
|
496
496
|
BASE_URL: 'https://billing.funnelfox.com',
|
|
497
497
|
REGION: 'default',
|
|
@@ -530,9 +530,9 @@ const API_ENDPOINTS = {
|
|
|
530
530
|
UPDATE_CLIENT_SESSION: '/v1/checkout/update_client_session',
|
|
531
531
|
CREATE_PAYMENT: '/v1/checkout/create_payment',
|
|
532
532
|
RESUME_PAYMENT: '/v1/checkout/resume_payment',
|
|
533
|
-
STRIPE_CREATE_PAYMENT: '/v1/stripe/create_payment',
|
|
534
533
|
ONE_CLICK: '/v1/checkout/one_click',
|
|
535
534
|
CREATE_SIMPLE_CLIENT_SESSION: '/v1/checkout/create_simple_client_session',
|
|
535
|
+
RECALCULATE_TAX: '/v1/checkout/recalculate_tax',
|
|
536
536
|
};
|
|
537
537
|
const ERROR_CODES = {
|
|
538
538
|
SDK_ERROR: 'SDK_ERROR',
|
|
@@ -1170,19 +1170,34 @@ class APIClient {
|
|
|
1170
1170
|
if (params.postalCode !== undefined) {
|
|
1171
1171
|
payload.postal_code = params.postalCode;
|
|
1172
1172
|
}
|
|
1173
|
+
if (params.subdivision !== undefined) {
|
|
1174
|
+
payload.subdivision = params.subdivision;
|
|
1175
|
+
}
|
|
1176
|
+
if (params.taxCalculationId !== undefined) {
|
|
1177
|
+
payload.tax_calculation_id = params.taxCalculationId;
|
|
1178
|
+
}
|
|
1173
1179
|
return (await this.request(API_ENDPOINTS.CREATE_PAYMENT, {
|
|
1174
1180
|
method: 'POST',
|
|
1175
1181
|
body: JSON.stringify(payload),
|
|
1176
1182
|
}));
|
|
1177
1183
|
}
|
|
1178
|
-
async
|
|
1179
|
-
|
|
1184
|
+
async recalculateTax(params) {
|
|
1185
|
+
const payload = {
|
|
1186
|
+
order_id: params.orderId,
|
|
1187
|
+
client_token: params.clientToken,
|
|
1188
|
+
country_code: params.countryCode,
|
|
1189
|
+
};
|
|
1190
|
+
if (params.postalCode) {
|
|
1191
|
+
payload.postal_code = params.postalCode;
|
|
1192
|
+
}
|
|
1193
|
+
if (params.subdivision) {
|
|
1194
|
+
payload.subdivision = params.subdivision;
|
|
1195
|
+
}
|
|
1196
|
+
const raw = (await this.request(API_ENDPOINTS.RECALCULATE_TAX, {
|
|
1180
1197
|
method: 'POST',
|
|
1181
|
-
body: JSON.stringify(
|
|
1182
|
-
order_id: params.orderId,
|
|
1183
|
-
payment_method_id: params.paymentMethodId,
|
|
1184
|
-
}),
|
|
1198
|
+
body: JSON.stringify(payload),
|
|
1185
1199
|
}));
|
|
1200
|
+
return raw.data;
|
|
1186
1201
|
}
|
|
1187
1202
|
async resumePayment(params) {
|
|
1188
1203
|
const payload = {
|
|
@@ -1763,6 +1778,7 @@ function getPageUrl() {
|
|
|
1763
1778
|
/**
|
|
1764
1779
|
* @fileoverview Checkout instance manager for Funnefox SDK
|
|
1765
1780
|
*/
|
|
1781
|
+
const TAX_RECALC_DEBOUNCE_MS = 600;
|
|
1766
1782
|
class CheckoutInstance extends EventEmitter {
|
|
1767
1783
|
constructor(config) {
|
|
1768
1784
|
super();
|
|
@@ -1798,11 +1814,71 @@ class CheckoutInstance extends EventEmitter {
|
|
|
1798
1814
|
if (!this.isPostalCodeVisible()) {
|
|
1799
1815
|
this.cardPostalCode = undefined;
|
|
1800
1816
|
}
|
|
1801
|
-
return;
|
|
1802
1817
|
}
|
|
1803
|
-
if (inputName === 'postalCode') {
|
|
1818
|
+
else if (inputName === 'postalCode') {
|
|
1804
1819
|
this.cardPostalCode = value?.trim() || undefined;
|
|
1805
1820
|
}
|
|
1821
|
+
if (this.isTaxEnabled()) {
|
|
1822
|
+
this.scheduleTaxRecalc();
|
|
1823
|
+
}
|
|
1824
|
+
};
|
|
1825
|
+
// Tax is driven by the tenant's tax_calculation_provider setting (surfaced as session.tax_enabled).
|
|
1826
|
+
// enableTax stays as a legacy host override only when the backend doesn't report the flag.
|
|
1827
|
+
this.isTaxEnabled = () => this.cachedSessionResponse?.data?.tax_enabled ??
|
|
1828
|
+
this.checkoutConfig.enableTax ??
|
|
1829
|
+
false;
|
|
1830
|
+
this.emitSessionTaxEstimate = () => {
|
|
1831
|
+
const data = this.cachedSessionResponse?.data;
|
|
1832
|
+
if (!data || data.amount_total == null) {
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
this.checkoutConfig.onTaxChange?.({
|
|
1836
|
+
amountTotal: data.amount_total,
|
|
1837
|
+
taxAmount: data.tax_amount ?? 0,
|
|
1838
|
+
currency: data.currency ?? '',
|
|
1839
|
+
});
|
|
1840
|
+
};
|
|
1841
|
+
this.runTaxRecalc = async () => {
|
|
1842
|
+
const orderId = this.orderId;
|
|
1843
|
+
const countryCode = this.getSelectedCountryCode();
|
|
1844
|
+
if (!orderId || !countryCode) {
|
|
1845
|
+
return;
|
|
1846
|
+
}
|
|
1847
|
+
try {
|
|
1848
|
+
const tax = await this.apiClient.recalculateTax({
|
|
1849
|
+
orderId,
|
|
1850
|
+
clientToken: this.cachedSessionResponse?.data?.client_token ?? '',
|
|
1851
|
+
countryCode,
|
|
1852
|
+
postalCode: this.cardPostalCode,
|
|
1853
|
+
});
|
|
1854
|
+
this.checkoutConfig.onTaxChange?.({
|
|
1855
|
+
amountTotal: tax.amount_total,
|
|
1856
|
+
taxAmount: tax.tax_amount,
|
|
1857
|
+
currency: tax.currency,
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1860
|
+
catch (err) {
|
|
1861
|
+
// Recalc failed for the new address; surface it instead of silently showing a stale total.
|
|
1862
|
+
console.warn('[funnelfox-billing] tax recalculation failed', err);
|
|
1863
|
+
this.checkoutConfig.onTaxError?.(err);
|
|
1864
|
+
}
|
|
1865
|
+
};
|
|
1866
|
+
this.scheduleTaxRecalc = () => {
|
|
1867
|
+
if (this.taxRecalcDebounce) {
|
|
1868
|
+
clearTimeout(this.taxRecalcDebounce);
|
|
1869
|
+
}
|
|
1870
|
+
this.taxRecalcDebounce = setTimeout(this.runTaxRecalc, TAX_RECALC_DEBOUNCE_MS);
|
|
1871
|
+
};
|
|
1872
|
+
// Flush a pending recalc so a card charge reflects the final entered address (no estimate drift).
|
|
1873
|
+
// Used for card payments only; wallets authorize their own amount and are left untouched.
|
|
1874
|
+
this.flushTaxRecalc = async () => {
|
|
1875
|
+
if (this.taxRecalcDebounce) {
|
|
1876
|
+
clearTimeout(this.taxRecalcDebounce);
|
|
1877
|
+
this.taxRecalcDebounce = undefined;
|
|
1878
|
+
}
|
|
1879
|
+
if (this.isTaxEnabled()) {
|
|
1880
|
+
await this.runTaxRecalc();
|
|
1881
|
+
}
|
|
1806
1882
|
};
|
|
1807
1883
|
this.handleMethodRender = (method) => {
|
|
1808
1884
|
this.emit(EVENTS.METHOD_RENDER, method);
|
|
@@ -1822,6 +1898,11 @@ class CheckoutInstance extends EventEmitter {
|
|
|
1822
1898
|
try {
|
|
1823
1899
|
this.onLoaderChangeWithRace(true);
|
|
1824
1900
|
this._setState('processing');
|
|
1901
|
+
// Card charge must reflect the final entered address; wallets authorize their own amount, so
|
|
1902
|
+
// only flush the pending recalc for card payments.
|
|
1903
|
+
if (this.activePaymentMethodType === exports.PaymentMethod.PAYMENT_CARD) {
|
|
1904
|
+
await this.flushTaxRecalc();
|
|
1905
|
+
}
|
|
1825
1906
|
const [radarSessionId, airwallexDeviceId] = await Promise.all([
|
|
1826
1907
|
this.cachedSessionResponse?.radarSessionId,
|
|
1827
1908
|
this.cachedSessionResponse?.airwallexDeviceId,
|
|
@@ -1939,6 +2020,9 @@ class CheckoutInstance extends EventEmitter {
|
|
|
1939
2020
|
await this.createSession();
|
|
1940
2021
|
await this._initializePrimerCheckout();
|
|
1941
2022
|
this._setState('ready');
|
|
2023
|
+
if (this.isTaxEnabled()) {
|
|
2024
|
+
this.emitSessionTaxEstimate();
|
|
2025
|
+
}
|
|
1942
2026
|
this.startUnhandledTelemetry();
|
|
1943
2027
|
this.checkoutConfig?.onInitialized?.();
|
|
1944
2028
|
return this;
|
|
@@ -2213,6 +2297,7 @@ class CheckoutInstance extends EventEmitter {
|
|
|
2213
2297
|
wasPaymentProcessedStarted = true;
|
|
2214
2298
|
},
|
|
2215
2299
|
onTokenizeShouldStart: data => {
|
|
2300
|
+
this.activePaymentMethodType = data.paymentMethodType;
|
|
2216
2301
|
this.emit(EVENTS.ERROR, undefined);
|
|
2217
2302
|
this.emit(EVENTS.START_PURCHASE, data.paymentMethodType);
|
|
2218
2303
|
return true;
|
|
@@ -2252,6 +2337,9 @@ class CheckoutInstance extends EventEmitter {
|
|
|
2252
2337
|
await this.primerWrapper.refreshClientSession();
|
|
2253
2338
|
this.onLoaderChangeWithRace(false);
|
|
2254
2339
|
this._setState('ready');
|
|
2340
|
+
if (this.isTaxEnabled()) {
|
|
2341
|
+
this.scheduleTaxRecalc();
|
|
2342
|
+
}
|
|
2255
2343
|
}
|
|
2256
2344
|
catch (error) {
|
|
2257
2345
|
this.onLoaderChangeWithRace(false);
|
|
@@ -2697,6 +2785,28 @@ async function createStripeCardForm(element, params) {
|
|
|
2697
2785
|
});
|
|
2698
2786
|
return mountStripeCardForm(element, session, { ...params, apiClient });
|
|
2699
2787
|
}
|
|
2788
|
+
async function createAdyenCardForm(element, params) {
|
|
2789
|
+
const config = resolveConfig(params, 'createAdyenCardForm');
|
|
2790
|
+
const [session, { mountAdyenCardForm }] = await Promise.all([
|
|
2791
|
+
sessionService.createSession({
|
|
2792
|
+
orgId: config.orgId,
|
|
2793
|
+
baseUrl: config.baseUrl,
|
|
2794
|
+
region: config.region,
|
|
2795
|
+
priceId: params.priceId,
|
|
2796
|
+
externalId: params.externalId,
|
|
2797
|
+
email: params.email,
|
|
2798
|
+
clientMetadata: params.clientMetadata,
|
|
2799
|
+
countryCode: params.countryCode,
|
|
2800
|
+
integration: 'adyen',
|
|
2801
|
+
}),
|
|
2802
|
+
Promise.resolve().then(function () { return require('./chunk-adyen-card-form.cjs.js'); }),
|
|
2803
|
+
]);
|
|
2804
|
+
const apiClient = new APIClient({
|
|
2805
|
+
orgId: config.orgId,
|
|
2806
|
+
baseUrl: config.baseUrl || DEFAULTS.BASE_URL,
|
|
2807
|
+
});
|
|
2808
|
+
return mountAdyenCardForm(element, session, { ...params, apiClient });
|
|
2809
|
+
}
|
|
2700
2810
|
async function purchaseStripeWallet(params) {
|
|
2701
2811
|
const config = resolveConfig(params, 'purchaseStripeWallet');
|
|
2702
2812
|
const [session, { purchaseWallet }] = await Promise.all([
|
|
@@ -2765,6 +2875,9 @@ const Billing = {
|
|
|
2765
2875
|
getAvailableWallet: getAvailableStripeWallet,
|
|
2766
2876
|
getAvailablePaymentMethods: getAvailableStripePaymentMethods,
|
|
2767
2877
|
},
|
|
2878
|
+
adyen: {
|
|
2879
|
+
createCardForm: createAdyenCardForm,
|
|
2880
|
+
},
|
|
2768
2881
|
};
|
|
2769
2882
|
if (typeof window !== 'undefined') {
|
|
2770
2883
|
window.Billing = Billing;
|
|
@@ -2788,4 +2901,6 @@ exports.configure = configure;
|
|
|
2788
2901
|
exports.createCheckout = createCheckout;
|
|
2789
2902
|
exports.createClientSession = createClientSession;
|
|
2790
2903
|
exports.getAvailablePaymentMethods = getAvailablePaymentMethods;
|
|
2904
|
+
exports.loadScript = loadScript$1;
|
|
2791
2905
|
exports.loadStripe = loadStripe;
|
|
2906
|
+
exports.loadStylesheet = loadStylesheet;
|
package/dist/chunk-index.es.js
CHANGED
|
@@ -489,7 +489,7 @@ var PaymentMethod;
|
|
|
489
489
|
/**
|
|
490
490
|
* @fileoverview Constants for Funnefox SDK
|
|
491
491
|
*/
|
|
492
|
-
const SDK_VERSION = '0.9.0-beta.
|
|
492
|
+
const SDK_VERSION = '0.9.0-beta.1';
|
|
493
493
|
const DEFAULTS = {
|
|
494
494
|
BASE_URL: 'https://billing.funnelfox.com',
|
|
495
495
|
REGION: 'default',
|
|
@@ -528,9 +528,9 @@ const API_ENDPOINTS = {
|
|
|
528
528
|
UPDATE_CLIENT_SESSION: '/v1/checkout/update_client_session',
|
|
529
529
|
CREATE_PAYMENT: '/v1/checkout/create_payment',
|
|
530
530
|
RESUME_PAYMENT: '/v1/checkout/resume_payment',
|
|
531
|
-
STRIPE_CREATE_PAYMENT: '/v1/stripe/create_payment',
|
|
532
531
|
ONE_CLICK: '/v1/checkout/one_click',
|
|
533
532
|
CREATE_SIMPLE_CLIENT_SESSION: '/v1/checkout/create_simple_client_session',
|
|
533
|
+
RECALCULATE_TAX: '/v1/checkout/recalculate_tax',
|
|
534
534
|
};
|
|
535
535
|
const ERROR_CODES = {
|
|
536
536
|
SDK_ERROR: 'SDK_ERROR',
|
|
@@ -1168,19 +1168,34 @@ class APIClient {
|
|
|
1168
1168
|
if (params.postalCode !== undefined) {
|
|
1169
1169
|
payload.postal_code = params.postalCode;
|
|
1170
1170
|
}
|
|
1171
|
+
if (params.subdivision !== undefined) {
|
|
1172
|
+
payload.subdivision = params.subdivision;
|
|
1173
|
+
}
|
|
1174
|
+
if (params.taxCalculationId !== undefined) {
|
|
1175
|
+
payload.tax_calculation_id = params.taxCalculationId;
|
|
1176
|
+
}
|
|
1171
1177
|
return (await this.request(API_ENDPOINTS.CREATE_PAYMENT, {
|
|
1172
1178
|
method: 'POST',
|
|
1173
1179
|
body: JSON.stringify(payload),
|
|
1174
1180
|
}));
|
|
1175
1181
|
}
|
|
1176
|
-
async
|
|
1177
|
-
|
|
1182
|
+
async recalculateTax(params) {
|
|
1183
|
+
const payload = {
|
|
1184
|
+
order_id: params.orderId,
|
|
1185
|
+
client_token: params.clientToken,
|
|
1186
|
+
country_code: params.countryCode,
|
|
1187
|
+
};
|
|
1188
|
+
if (params.postalCode) {
|
|
1189
|
+
payload.postal_code = params.postalCode;
|
|
1190
|
+
}
|
|
1191
|
+
if (params.subdivision) {
|
|
1192
|
+
payload.subdivision = params.subdivision;
|
|
1193
|
+
}
|
|
1194
|
+
const raw = (await this.request(API_ENDPOINTS.RECALCULATE_TAX, {
|
|
1178
1195
|
method: 'POST',
|
|
1179
|
-
body: JSON.stringify(
|
|
1180
|
-
order_id: params.orderId,
|
|
1181
|
-
payment_method_id: params.paymentMethodId,
|
|
1182
|
-
}),
|
|
1196
|
+
body: JSON.stringify(payload),
|
|
1183
1197
|
}));
|
|
1198
|
+
return raw.data;
|
|
1184
1199
|
}
|
|
1185
1200
|
async resumePayment(params) {
|
|
1186
1201
|
const payload = {
|
|
@@ -1761,6 +1776,7 @@ function getPageUrl() {
|
|
|
1761
1776
|
/**
|
|
1762
1777
|
* @fileoverview Checkout instance manager for Funnefox SDK
|
|
1763
1778
|
*/
|
|
1779
|
+
const TAX_RECALC_DEBOUNCE_MS = 600;
|
|
1764
1780
|
class CheckoutInstance extends EventEmitter {
|
|
1765
1781
|
constructor(config) {
|
|
1766
1782
|
super();
|
|
@@ -1796,11 +1812,71 @@ class CheckoutInstance extends EventEmitter {
|
|
|
1796
1812
|
if (!this.isPostalCodeVisible()) {
|
|
1797
1813
|
this.cardPostalCode = undefined;
|
|
1798
1814
|
}
|
|
1799
|
-
return;
|
|
1800
1815
|
}
|
|
1801
|
-
if (inputName === 'postalCode') {
|
|
1816
|
+
else if (inputName === 'postalCode') {
|
|
1802
1817
|
this.cardPostalCode = value?.trim() || undefined;
|
|
1803
1818
|
}
|
|
1819
|
+
if (this.isTaxEnabled()) {
|
|
1820
|
+
this.scheduleTaxRecalc();
|
|
1821
|
+
}
|
|
1822
|
+
};
|
|
1823
|
+
// Tax is driven by the tenant's tax_calculation_provider setting (surfaced as session.tax_enabled).
|
|
1824
|
+
// enableTax stays as a legacy host override only when the backend doesn't report the flag.
|
|
1825
|
+
this.isTaxEnabled = () => this.cachedSessionResponse?.data?.tax_enabled ??
|
|
1826
|
+
this.checkoutConfig.enableTax ??
|
|
1827
|
+
false;
|
|
1828
|
+
this.emitSessionTaxEstimate = () => {
|
|
1829
|
+
const data = this.cachedSessionResponse?.data;
|
|
1830
|
+
if (!data || data.amount_total == null) {
|
|
1831
|
+
return;
|
|
1832
|
+
}
|
|
1833
|
+
this.checkoutConfig.onTaxChange?.({
|
|
1834
|
+
amountTotal: data.amount_total,
|
|
1835
|
+
taxAmount: data.tax_amount ?? 0,
|
|
1836
|
+
currency: data.currency ?? '',
|
|
1837
|
+
});
|
|
1838
|
+
};
|
|
1839
|
+
this.runTaxRecalc = async () => {
|
|
1840
|
+
const orderId = this.orderId;
|
|
1841
|
+
const countryCode = this.getSelectedCountryCode();
|
|
1842
|
+
if (!orderId || !countryCode) {
|
|
1843
|
+
return;
|
|
1844
|
+
}
|
|
1845
|
+
try {
|
|
1846
|
+
const tax = await this.apiClient.recalculateTax({
|
|
1847
|
+
orderId,
|
|
1848
|
+
clientToken: this.cachedSessionResponse?.data?.client_token ?? '',
|
|
1849
|
+
countryCode,
|
|
1850
|
+
postalCode: this.cardPostalCode,
|
|
1851
|
+
});
|
|
1852
|
+
this.checkoutConfig.onTaxChange?.({
|
|
1853
|
+
amountTotal: tax.amount_total,
|
|
1854
|
+
taxAmount: tax.tax_amount,
|
|
1855
|
+
currency: tax.currency,
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
catch (err) {
|
|
1859
|
+
// Recalc failed for the new address; surface it instead of silently showing a stale total.
|
|
1860
|
+
console.warn('[funnelfox-billing] tax recalculation failed', err);
|
|
1861
|
+
this.checkoutConfig.onTaxError?.(err);
|
|
1862
|
+
}
|
|
1863
|
+
};
|
|
1864
|
+
this.scheduleTaxRecalc = () => {
|
|
1865
|
+
if (this.taxRecalcDebounce) {
|
|
1866
|
+
clearTimeout(this.taxRecalcDebounce);
|
|
1867
|
+
}
|
|
1868
|
+
this.taxRecalcDebounce = setTimeout(this.runTaxRecalc, TAX_RECALC_DEBOUNCE_MS);
|
|
1869
|
+
};
|
|
1870
|
+
// Flush a pending recalc so a card charge reflects the final entered address (no estimate drift).
|
|
1871
|
+
// Used for card payments only; wallets authorize their own amount and are left untouched.
|
|
1872
|
+
this.flushTaxRecalc = async () => {
|
|
1873
|
+
if (this.taxRecalcDebounce) {
|
|
1874
|
+
clearTimeout(this.taxRecalcDebounce);
|
|
1875
|
+
this.taxRecalcDebounce = undefined;
|
|
1876
|
+
}
|
|
1877
|
+
if (this.isTaxEnabled()) {
|
|
1878
|
+
await this.runTaxRecalc();
|
|
1879
|
+
}
|
|
1804
1880
|
};
|
|
1805
1881
|
this.handleMethodRender = (method) => {
|
|
1806
1882
|
this.emit(EVENTS.METHOD_RENDER, method);
|
|
@@ -1820,6 +1896,11 @@ class CheckoutInstance extends EventEmitter {
|
|
|
1820
1896
|
try {
|
|
1821
1897
|
this.onLoaderChangeWithRace(true);
|
|
1822
1898
|
this._setState('processing');
|
|
1899
|
+
// Card charge must reflect the final entered address; wallets authorize their own amount, so
|
|
1900
|
+
// only flush the pending recalc for card payments.
|
|
1901
|
+
if (this.activePaymentMethodType === PaymentMethod.PAYMENT_CARD) {
|
|
1902
|
+
await this.flushTaxRecalc();
|
|
1903
|
+
}
|
|
1823
1904
|
const [radarSessionId, airwallexDeviceId] = await Promise.all([
|
|
1824
1905
|
this.cachedSessionResponse?.radarSessionId,
|
|
1825
1906
|
this.cachedSessionResponse?.airwallexDeviceId,
|
|
@@ -1937,6 +2018,9 @@ class CheckoutInstance extends EventEmitter {
|
|
|
1937
2018
|
await this.createSession();
|
|
1938
2019
|
await this._initializePrimerCheckout();
|
|
1939
2020
|
this._setState('ready');
|
|
2021
|
+
if (this.isTaxEnabled()) {
|
|
2022
|
+
this.emitSessionTaxEstimate();
|
|
2023
|
+
}
|
|
1940
2024
|
this.startUnhandledTelemetry();
|
|
1941
2025
|
this.checkoutConfig?.onInitialized?.();
|
|
1942
2026
|
return this;
|
|
@@ -2211,6 +2295,7 @@ class CheckoutInstance extends EventEmitter {
|
|
|
2211
2295
|
wasPaymentProcessedStarted = true;
|
|
2212
2296
|
},
|
|
2213
2297
|
onTokenizeShouldStart: data => {
|
|
2298
|
+
this.activePaymentMethodType = data.paymentMethodType;
|
|
2214
2299
|
this.emit(EVENTS.ERROR, undefined);
|
|
2215
2300
|
this.emit(EVENTS.START_PURCHASE, data.paymentMethodType);
|
|
2216
2301
|
return true;
|
|
@@ -2250,6 +2335,9 @@ class CheckoutInstance extends EventEmitter {
|
|
|
2250
2335
|
await this.primerWrapper.refreshClientSession();
|
|
2251
2336
|
this.onLoaderChangeWithRace(false);
|
|
2252
2337
|
this._setState('ready');
|
|
2338
|
+
if (this.isTaxEnabled()) {
|
|
2339
|
+
this.scheduleTaxRecalc();
|
|
2340
|
+
}
|
|
2253
2341
|
}
|
|
2254
2342
|
catch (error) {
|
|
2255
2343
|
this.onLoaderChangeWithRace(false);
|
|
@@ -2695,6 +2783,28 @@ async function createStripeCardForm(element, params) {
|
|
|
2695
2783
|
});
|
|
2696
2784
|
return mountStripeCardForm(element, session, { ...params, apiClient });
|
|
2697
2785
|
}
|
|
2786
|
+
async function createAdyenCardForm(element, params) {
|
|
2787
|
+
const config = resolveConfig(params, 'createAdyenCardForm');
|
|
2788
|
+
const [session, { mountAdyenCardForm }] = await Promise.all([
|
|
2789
|
+
sessionService.createSession({
|
|
2790
|
+
orgId: config.orgId,
|
|
2791
|
+
baseUrl: config.baseUrl,
|
|
2792
|
+
region: config.region,
|
|
2793
|
+
priceId: params.priceId,
|
|
2794
|
+
externalId: params.externalId,
|
|
2795
|
+
email: params.email,
|
|
2796
|
+
clientMetadata: params.clientMetadata,
|
|
2797
|
+
countryCode: params.countryCode,
|
|
2798
|
+
integration: 'adyen',
|
|
2799
|
+
}),
|
|
2800
|
+
import('./chunk-adyen-card-form.es.js'),
|
|
2801
|
+
]);
|
|
2802
|
+
const apiClient = new APIClient({
|
|
2803
|
+
orgId: config.orgId,
|
|
2804
|
+
baseUrl: config.baseUrl || DEFAULTS.BASE_URL,
|
|
2805
|
+
});
|
|
2806
|
+
return mountAdyenCardForm(element, session, { ...params, apiClient });
|
|
2807
|
+
}
|
|
2698
2808
|
async function purchaseStripeWallet(params) {
|
|
2699
2809
|
const config = resolveConfig(params, 'purchaseStripeWallet');
|
|
2700
2810
|
const [session, { purchaseWallet }] = await Promise.all([
|
|
@@ -2763,9 +2873,12 @@ const Billing = {
|
|
|
2763
2873
|
getAvailableWallet: getAvailableStripeWallet,
|
|
2764
2874
|
getAvailablePaymentMethods: getAvailableStripePaymentMethods,
|
|
2765
2875
|
},
|
|
2876
|
+
adyen: {
|
|
2877
|
+
createCardForm: createAdyenCardForm,
|
|
2878
|
+
},
|
|
2766
2879
|
};
|
|
2767
2880
|
if (typeof window !== 'undefined') {
|
|
2768
2881
|
window.Billing = Billing;
|
|
2769
2882
|
}
|
|
2770
2883
|
|
|
2771
|
-
export { APIError as A, Billing as B, CheckoutError as C, DEFAULT_BUTTONS_OPTIONS as D, EVENTS as E, FunnefoxSDKError as F, NetworkError as N, PaymentMethod as P, SDK_VERSION as S, ValidationError as V,
|
|
2884
|
+
export { APIError as A, Billing as B, CheckoutError as C, DEFAULT_BUTTONS_OPTIONS as D, EVENTS as E, FunnefoxSDKError as F, NetworkError as N, PaymentMethod as P, SDK_VERSION as S, ValidationError as V, loadScript$1 as a, loadStripe as b, PrimerError as c, ConfigurationError as d, DEFAULTS as e, CHECKOUT_STATES as f, ERROR_CODES as g, configure as h, createCheckout as i, createClientSession as j, getAvailablePaymentMethods as k, loadStylesheet as l };
|
|
@@ -10,17 +10,22 @@
|
|
|
10
10
|
var stripeLoader = require('./chunk-stripe-loader.cjs.js');
|
|
11
11
|
require('./chunk-index.cjs.js');
|
|
12
12
|
|
|
13
|
+
const TAX_RECALC_DEBOUNCE_MS = 600;
|
|
13
14
|
async function mountStripeCardForm(element, session, params) {
|
|
14
|
-
const { stripe_public_key, amount, currency, order_id } = session.data;
|
|
15
|
+
const { stripe_public_key, amount, currency, order_id, is_link_enabled } = session.data;
|
|
16
|
+
// Tax flow is driven by the tenant setting (session.tax_enabled); enableTax is a legacy override.
|
|
17
|
+
const taxEnabled = session.data.tax_enabled ?? params.enableTax ?? false;
|
|
15
18
|
const stripe = await stripeLoader.getStripe(stripe_public_key);
|
|
16
19
|
if (!stripe)
|
|
17
20
|
throw new Error('Failed to load Stripe');
|
|
21
|
+
// Remounting (e.g. picking another price) must replace the form, not stack a second one.
|
|
22
|
+
element.replaceChildren();
|
|
18
23
|
const stripeElements = stripe.elements({
|
|
19
24
|
mode: 'subscription',
|
|
20
25
|
amount,
|
|
21
26
|
currency,
|
|
22
27
|
paymentMethodCreation: 'manual',
|
|
23
|
-
paymentMethodTypes: ['card', 'link'],
|
|
28
|
+
paymentMethodTypes: is_link_enabled ? ['card', 'link'] : ['card'],
|
|
24
29
|
appearance: params.appearance,
|
|
25
30
|
});
|
|
26
31
|
const paymentElement = stripeElements.create('payment', {
|
|
@@ -31,7 +36,81 @@ async function mountStripeCardForm(element, session, params) {
|
|
|
31
36
|
},
|
|
32
37
|
terms: { card: 'never' },
|
|
33
38
|
});
|
|
34
|
-
|
|
39
|
+
// Tax mode mounts Stripe's Address Element (its change events are readable, so tax recalculates live
|
|
40
|
+
// as the buyer edits the address) above the Payment Element. The country is pre-filled from the
|
|
41
|
+
// detected location so tax is computed from the address (not a separate IP estimate); the resulting
|
|
42
|
+
// calculation id is attached to the PaymentIntent at payment time so Stripe records the tax.
|
|
43
|
+
let taxCalculationId;
|
|
44
|
+
let taxAddress = {};
|
|
45
|
+
// Flushed on submit so the charge always reflects the final entered address (no estimate drift).
|
|
46
|
+
let flushTaxRecalc = async () => { };
|
|
47
|
+
if (taxEnabled) {
|
|
48
|
+
const addressContainer = document.createElement('div');
|
|
49
|
+
const paymentContainer = document.createElement('div');
|
|
50
|
+
element.appendChild(addressContainer);
|
|
51
|
+
element.appendChild(paymentContainer);
|
|
52
|
+
const addressElement = stripeElements.create('address', {
|
|
53
|
+
mode: 'billing',
|
|
54
|
+
fields: { phone: 'never' },
|
|
55
|
+
...(session.data.detected_country_code
|
|
56
|
+
? {
|
|
57
|
+
defaultValues: {
|
|
58
|
+
address: { country: session.data.detected_country_code },
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
: {}),
|
|
62
|
+
});
|
|
63
|
+
addressElement.mount(addressContainer);
|
|
64
|
+
paymentElement.mount(paymentContainer);
|
|
65
|
+
const runRecalc = async () => {
|
|
66
|
+
const country = taxAddress.country;
|
|
67
|
+
if (!country)
|
|
68
|
+
return;
|
|
69
|
+
try {
|
|
70
|
+
const tax = await params.apiClient.recalculateTax({
|
|
71
|
+
orderId: order_id,
|
|
72
|
+
clientToken: session.data.client_token,
|
|
73
|
+
countryCode: country,
|
|
74
|
+
postalCode: taxAddress.postalCode,
|
|
75
|
+
subdivision: taxAddress.state,
|
|
76
|
+
});
|
|
77
|
+
taxCalculationId = tax.tax_calculation_id;
|
|
78
|
+
stripeElements.update({ amount: tax.amount_total });
|
|
79
|
+
params.onTaxChange?.({
|
|
80
|
+
amountTotal: tax.amount_total,
|
|
81
|
+
taxAmount: tax.tax_amount,
|
|
82
|
+
currency: tax.currency,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Best-effort: keep the current total if recalculation fails.
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
let debounce;
|
|
90
|
+
flushTaxRecalc = async () => {
|
|
91
|
+
if (debounce) {
|
|
92
|
+
clearTimeout(debounce);
|
|
93
|
+
debounce = undefined;
|
|
94
|
+
}
|
|
95
|
+
await runRecalc();
|
|
96
|
+
};
|
|
97
|
+
addressElement.on('change', event => {
|
|
98
|
+
const address = event.value.address;
|
|
99
|
+
taxAddress = {
|
|
100
|
+
country: address.country,
|
|
101
|
+
postalCode: address.postal_code || undefined,
|
|
102
|
+
state: address.state || undefined,
|
|
103
|
+
};
|
|
104
|
+
if (!taxAddress.country)
|
|
105
|
+
return;
|
|
106
|
+
if (debounce)
|
|
107
|
+
clearTimeout(debounce);
|
|
108
|
+
debounce = setTimeout(runRecalc, TAX_RECALC_DEBOUNCE_MS);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
paymentElement.mount(element);
|
|
113
|
+
}
|
|
35
114
|
await new Promise((resolve, reject) => {
|
|
36
115
|
paymentElement.once('ready', () => resolve());
|
|
37
116
|
// 'loaderror' is a valid Stripe event but not yet in the @stripe/stripe-js types
|
|
@@ -42,6 +121,7 @@ async function mountStripeCardForm(element, session, params) {
|
|
|
42
121
|
submit: async () => {
|
|
43
122
|
params.onLoaderChange?.(true);
|
|
44
123
|
try {
|
|
124
|
+
await flushTaxRecalc();
|
|
45
125
|
const { error: submitError } = await stripeElements.submit();
|
|
46
126
|
if (submitError)
|
|
47
127
|
throw submitError;
|
|
@@ -50,19 +130,23 @@ async function mountStripeCardForm(element, session, params) {
|
|
|
50
130
|
});
|
|
51
131
|
if (error)
|
|
52
132
|
throw error;
|
|
53
|
-
const raw = await params.apiClient.
|
|
133
|
+
const raw = await params.apiClient.createPayment({
|
|
54
134
|
orderId: order_id,
|
|
55
|
-
|
|
135
|
+
paymentMethodToken: paymentMethod.id,
|
|
136
|
+
email: params.email,
|
|
137
|
+
countryCode: taxEnabled ? taxAddress.country : params.countryCode,
|
|
138
|
+
postalCode: taxEnabled ? taxAddress.postalCode : undefined,
|
|
139
|
+
subdivision: taxEnabled ? taxAddress.state : undefined,
|
|
140
|
+
taxCalculationId: taxEnabled ? taxCalculationId : undefined,
|
|
141
|
+
clientMetadata: params.clientMetadata,
|
|
56
142
|
});
|
|
57
143
|
const result = params.apiClient.processPaymentResponse(raw);
|
|
58
144
|
if (result.type === 'action_required') {
|
|
59
|
-
const { error:
|
|
145
|
+
const { error: actionError } = await stripe.handleNextAction({
|
|
60
146
|
clientSecret: result.clientToken,
|
|
61
|
-
redirect: 'if_required',
|
|
62
|
-
confirmParams: { return_url: window.location.href },
|
|
63
147
|
});
|
|
64
|
-
if (
|
|
65
|
-
throw
|
|
148
|
+
if (actionError)
|
|
149
|
+
throw actionError;
|
|
66
150
|
}
|
|
67
151
|
params.onPaymentSuccess?.(paymentMethod, order_id);
|
|
68
152
|
}
|