@tagadapay/headless-sdk 1.8.0-rc.0 → 1.8.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.
Files changed (66) hide show
  1. package/README.md +216 -1
  2. package/dist/core/client.d.ts +68 -0
  3. package/dist/core/client.js +71 -0
  4. package/dist/core/errors.d.ts +46 -0
  5. package/dist/core/errors.js +57 -0
  6. package/dist/core/helpers/autocomplete.d.ts +89 -0
  7. package/dist/core/helpers/autocomplete.js +186 -0
  8. package/dist/core/helpers/countries.d.ts +45 -0
  9. package/dist/core/helpers/countries.js +65 -0
  10. package/dist/core/helpers/currency.d.ts +26 -0
  11. package/dist/core/helpers/currency.js +72 -0
  12. package/dist/core/helpers/index.d.ts +5 -0
  13. package/dist/core/helpers/index.js +5 -0
  14. package/dist/core/helpers/iso3166.d.ts +28 -0
  15. package/dist/core/helpers/iso3166.js +84 -0
  16. package/dist/core/http.d.ts +27 -0
  17. package/dist/core/http.js +116 -0
  18. package/dist/core/index.d.ts +15 -0
  19. package/dist/core/index.js +13 -0
  20. package/dist/core/modules/catalog.d.ts +27 -0
  21. package/dist/core/modules/catalog.js +38 -0
  22. package/dist/core/modules/checkout.d.ts +268 -0
  23. package/dist/core/modules/checkout.js +470 -0
  24. package/dist/core/modules/customer.d.ts +33 -0
  25. package/dist/core/modules/customer.js +53 -0
  26. package/dist/core/modules/funnel.d.ts +38 -0
  27. package/dist/core/modules/funnel.js +138 -0
  28. package/dist/core/modules/offers.d.ts +137 -0
  29. package/dist/core/modules/offers.js +141 -0
  30. package/dist/core/modules/payment.d.ts +298 -0
  31. package/dist/core/modules/payment.js +701 -0
  32. package/dist/core/modules/rx.d.ts +162 -0
  33. package/dist/core/modules/rx.js +46 -0
  34. package/dist/core/types.d.ts +505 -0
  35. package/dist/core/types.js +5 -0
  36. package/dist/react/hooks/useCatalog.d.ts +25 -0
  37. package/dist/react/hooks/useCatalog.js +58 -0
  38. package/dist/react/hooks/useCheckout.d.ts +58 -0
  39. package/dist/react/hooks/useCheckout.js +177 -0
  40. package/dist/react/hooks/useCustomer.d.ts +28 -0
  41. package/dist/react/hooks/useCustomer.js +77 -0
  42. package/dist/react/hooks/useGoogleAutocomplete.d.ts +26 -0
  43. package/dist/react/hooks/useGoogleAutocomplete.js +70 -0
  44. package/dist/react/hooks/useISOData.d.ts +23 -0
  45. package/dist/react/hooks/useISOData.js +63 -0
  46. package/dist/react/hooks/useOffers.d.ts +134 -0
  47. package/dist/react/hooks/useOffers.js +171 -0
  48. package/dist/react/hooks/usePayment.d.ts +108 -0
  49. package/dist/react/hooks/usePayment.js +233 -0
  50. package/dist/react/hooks/useRx.d.ts +64 -0
  51. package/dist/react/hooks/useRx.js +145 -0
  52. package/dist/react/index.d.ts +22 -0
  53. package/dist/react/index.js +12 -0
  54. package/dist/react/internal/redirectGuard.d.ts +9 -0
  55. package/dist/react/internal/redirectGuard.js +25 -0
  56. package/dist/react/provider.d.ts +31 -0
  57. package/dist/react/provider.js +32 -0
  58. package/dist/stripe/StripeExpressButton.d.ts +30 -0
  59. package/dist/stripe/StripeExpressButton.js +32 -0
  60. package/dist/stripe/StripeExpressCheckout.d.ts +98 -0
  61. package/dist/stripe/StripeExpressCheckout.js +198 -0
  62. package/dist/stripe/index.d.ts +8 -0
  63. package/dist/stripe/index.js +15 -0
  64. package/dist/stripe/useStripeExpress.d.ts +54 -0
  65. package/dist/stripe/useStripeExpress.js +126 -0
  66. package/package.json +2 -3
@@ -0,0 +1,198 @@
1
+ const EXPRESS_METHOD_KEYS = ['apple_pay', 'google_pay', 'paypal', 'link', 'klarna_express'];
2
+ /**
3
+ * Session-based Stripe Express Checkout integration.
4
+ *
5
+ * Mounts the Stripe `ExpressCheckoutElement` (Apple Pay, Google Pay, Link,
6
+ * PayPal, Klarna express) into any DOM container, wired to a Tagada
7
+ * checkout session. Drives the full flow:
8
+ * mount → wallet confirm → backend `payDirect` (`processStripeExpress`)
9
+ * → `stripe.confirmPayment` → poll payment status → emit `success`.
10
+ *
11
+ * **Session-free / partner S2S flow?** Use the lower-level primitive
12
+ * `startStripeExpressCheckout` exported from `@tagadapay/core-js`
13
+ * (also re-exported from `@tagadapay/headless-sdk/stripe`). It uses
14
+ * Stripe's older Payment Request API, stops at tokenization, and
15
+ * returns a `paymentMethodId` for partner-server-side processing.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import { createHeadlessClient } from '@tagadapay/headless-sdk';
20
+ * import { StripeExpressCheckout } from '@tagadapay/headless-sdk/stripe';
21
+ *
22
+ * const client = createHeadlessClient({ storeId: 'store_xxx' });
23
+ * const session = await client.checkout.loadSession(checkoutToken);
24
+ * const express = await client.payment.getExpressMethods(session.id);
25
+ *
26
+ * const ece = new StripeExpressCheckout({
27
+ * client,
28
+ * publishableKey: express.applePay?.publishableKey!,
29
+ * processorId: express.applePay?.processorId!,
30
+ * });
31
+ *
32
+ * ece.on('success', ({ payment }) => console.log('Paid!', payment.id));
33
+ * ece.on('error', (msg) => console.error(msg));
34
+ *
35
+ * await ece.mount(document.getElementById('express-container')!, {
36
+ * checkoutSessionId: session.id,
37
+ * amount: session.totals.total,
38
+ * currency: session.totals.currency,
39
+ * });
40
+ * ```
41
+ */
42
+ export class StripeExpressCheckout {
43
+ constructor(opts) {
44
+ this.stripe = null;
45
+ this.elements = null;
46
+ this.expressElement = null;
47
+ this.container = null;
48
+ this.isProcessing = false;
49
+ this.listeners = {};
50
+ this.client = opts.client;
51
+ this.publishableKey = opts.publishableKey;
52
+ this.processorId = opts.processorId;
53
+ this.enabledMethods = opts.enabledMethods ?? [...EXPRESS_METHOD_KEYS];
54
+ this.buttonHeight = opts.buttonHeight ?? 48;
55
+ this.buttonType = opts.buttonType ?? 'buy';
56
+ this.emailRequired = opts.emailRequired ?? true;
57
+ this.billingAddressRequired = opts.billingAddressRequired ?? true;
58
+ }
59
+ on(event, handler) {
60
+ if (!this.listeners[event]) {
61
+ this.listeners[event] = new Set();
62
+ }
63
+ this.listeners[event].add(handler);
64
+ }
65
+ off(event, handler) {
66
+ this.listeners[event]?.delete(handler);
67
+ }
68
+ /**
69
+ * Load Stripe.js, create Elements, and mount the ExpressCheckoutElement.
70
+ */
71
+ async mount(container, opts) {
72
+ this.container = container;
73
+ const { loadStripe } = await import('@stripe/stripe-js');
74
+ const stripe = await loadStripe(this.publishableKey);
75
+ if (!stripe) {
76
+ this.emit('error', 'Failed to load Stripe.js');
77
+ return;
78
+ }
79
+ this.stripe = stripe;
80
+ const amount = Math.max(Math.round(opts.amount), 1);
81
+ const currency = opts.currency.toLowerCase();
82
+ this.elements = stripe.elements({
83
+ mode: 'payment',
84
+ amount,
85
+ currency,
86
+ });
87
+ const methodEnabled = (key) => this.enabledMethods.includes(key);
88
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Stripe TS types don't expose expressCheckout overload
89
+ this.expressElement = this.elements.create('expressCheckout', {
90
+ buttonType: {
91
+ applePay: this.buttonType,
92
+ googlePay: this.buttonType,
93
+ },
94
+ buttonHeight: this.buttonHeight,
95
+ paymentMethods: {
96
+ applePay: methodEnabled('apple_pay') ? 'always' : 'never',
97
+ googlePay: methodEnabled('google_pay') ? 'always' : 'never',
98
+ link: methodEnabled('link') ? 'auto' : 'never',
99
+ paypal: methodEnabled('paypal') ? 'auto' : 'never',
100
+ klarna: methodEnabled('klarna_express') ? 'auto' : 'never',
101
+ },
102
+ emailRequired: this.emailRequired,
103
+ billingAddressRequired: this.billingAddressRequired,
104
+ });
105
+ this.expressElement.on('ready', ({ availablePaymentMethods }) => {
106
+ if (availablePaymentMethods) {
107
+ this.emit('ready', { availablePaymentMethods });
108
+ }
109
+ });
110
+ this.expressElement.on('cancel', () => {
111
+ this.isProcessing = false;
112
+ this.emit('cancel', undefined);
113
+ });
114
+ this.expressElement.on('confirm', async (event) => {
115
+ if (this.isProcessing)
116
+ return;
117
+ this.isProcessing = true;
118
+ try {
119
+ const { error: submitError } = await this.elements.submit();
120
+ if (submitError) {
121
+ this.emit('error', submitError.message ?? 'Validation failed');
122
+ this.isProcessing = false;
123
+ return;
124
+ }
125
+ const payResult = await this.client.payment.processStripeExpress({
126
+ checkoutSessionId: opts.checkoutSessionId,
127
+ processorId: this.processorId,
128
+ paymentMethod: event.expressPaymentType,
129
+ });
130
+ const clientSecret = this.extractClientSecret(payResult);
131
+ if (!clientSecret) {
132
+ this.emit('error', 'Express checkout configuration missing — no client secret returned');
133
+ this.isProcessing = false;
134
+ return;
135
+ }
136
+ const { error: confirmError } = await this.stripe.confirmPayment({
137
+ elements: this.elements,
138
+ clientSecret,
139
+ confirmParams: { return_url: window.location.href },
140
+ redirect: 'if_required',
141
+ });
142
+ if (confirmError) {
143
+ this.emit('error', confirmError.message ?? 'Payment confirmation failed');
144
+ this.isProcessing = false;
145
+ return;
146
+ }
147
+ const paymentId = payResult.payment.id;
148
+ const status = await this.client.payment.pollPaymentStatus(paymentId);
149
+ if (status.status === 'succeeded') {
150
+ this.emit('success', { payment: status, order: status.order });
151
+ }
152
+ else {
153
+ this.emit('error', `Payment ${status.status}`);
154
+ }
155
+ }
156
+ catch (err) {
157
+ const msg = err instanceof Error ? err.message : 'Express checkout failed';
158
+ this.emit('error', msg);
159
+ }
160
+ finally {
161
+ this.isProcessing = false;
162
+ }
163
+ });
164
+ this.expressElement.mount(container);
165
+ }
166
+ /**
167
+ * Update the payment amount (e.g. after a cart change).
168
+ */
169
+ updateAmount(amount, currency) {
170
+ if (!this.elements)
171
+ return;
172
+ const update = { amount: Math.max(Math.round(amount), 1) };
173
+ if (currency)
174
+ update.currency = currency.toLowerCase();
175
+ this.elements.update(update);
176
+ }
177
+ /**
178
+ * Clean up: unmount the element and release references.
179
+ */
180
+ unmount() {
181
+ this.expressElement?.unmount();
182
+ this.expressElement = null;
183
+ this.elements = null;
184
+ this.stripe = null;
185
+ this.container = null;
186
+ this.listeners = {};
187
+ }
188
+ emit(event, data) {
189
+ this.listeners[event]?.forEach((handler) => handler(data));
190
+ }
191
+ extractClientSecret(payResult) {
192
+ return payResult.payment?.requireActionData?.metadata?.stripeExpressCheckout
193
+ ? (payResult.payment.requireActionData.metadata
194
+ .stripeExpressCheckout.clientSecret)
195
+ : undefined;
196
+ }
197
+ }
198
+ //# sourceMappingURL=StripeExpressCheckout.js.map
@@ -0,0 +1,8 @@
1
+ export { StripeExpressCheckout } from './StripeExpressCheckout.js';
2
+ export { useStripeExpress } from './useStripeExpress.js';
3
+ export type { UseStripeExpressOptions, UseStripeExpressReturn } from './useStripeExpress.js';
4
+ export { StripeExpressButton } from './StripeExpressButton.js';
5
+ export type { StripeExpressButtonProps } from './StripeExpressButton.js';
6
+ export type { StripeExpressOptions } from '../core/types.js';
7
+ export { startStripeExpressCheckout, isStripeExpressAvailable, type StripeExpressOptions as CoreStripeExpressOptions, type StripeExpressShippingAddress, type StripeExpressTokenResult, type StripeExpressCallbacks, } from '@tagadapay/core-js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,15 @@
1
+ // ─────────────────────────────────────────────────────────────────
2
+ // Session-based Stripe Express Checkout — drives Stripe's Express
3
+ // Checkout Element (drop-in widget) wired to a checkout session.
4
+ // Used by sites built on `@tagadapay/headless-sdk`.
5
+ // ─────────────────────────────────────────────────────────────────
6
+ export { StripeExpressCheckout } from './StripeExpressCheckout.js';
7
+ export { useStripeExpress } from './useStripeExpress.js';
8
+ export { StripeExpressButton } from './StripeExpressButton.js';
9
+ // ─────────────────────────────────────────────────────────────────
10
+ // Session-free wallet tokenization — re-exported from `@tagadapay/core-js`
11
+ // for partner / S2S flows that don't use a checkout session. Stops at
12
+ // Stripe `paymentMethodId`; the partner forwards it to their server.
13
+ // ─────────────────────────────────────────────────────────────────
14
+ export { startStripeExpressCheckout, isStripeExpressAvailable, } from '@tagadapay/core-js';
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,54 @@
1
+ import type { ExpressMethods, PaymentStatus, PaymentSetupConfig } from '../core/types.js';
2
+ export interface UseStripeExpressOptions {
3
+ checkoutSessionId: string;
4
+ amount: number;
5
+ currency: string;
6
+ onSuccess?: (result: {
7
+ payment: PaymentStatus;
8
+ order?: {
9
+ id: string;
10
+ checkoutSessionId?: string;
11
+ };
12
+ }) => void;
13
+ onError?: (error: string) => void;
14
+ onCancel?: () => void;
15
+ /** Override payment setup config (skips API discovery). */
16
+ paymentSetupConfig?: PaymentSetupConfig;
17
+ }
18
+ export interface UseStripeExpressReturn {
19
+ /** Attach this ref to a <div> — the express buttons will mount inside it. */
20
+ containerRef: React.RefObject<HTMLDivElement | null>;
21
+ /** True once the Stripe element has rendered at least one wallet button. */
22
+ isReady: boolean;
23
+ /** True while a payment is being confirmed. */
24
+ isProcessing: boolean;
25
+ /** Discovered express methods (null while loading). */
26
+ expressMethods: ExpressMethods | null;
27
+ /** Update the displayed amount after cart changes. */
28
+ updateAmount: (amount: number, currency?: string) => void;
29
+ }
30
+ /**
31
+ * React hook that mounts the Stripe Express Checkout Element into a ref'd container.
32
+ *
33
+ * Handles the full lifecycle: discover config -> load Stripe -> mount ECE ->
34
+ * onConfirm -> processStripeExpress -> stripe.confirmPayment -> poll -> callbacks.
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * import { useStripeExpress } from '@tagadapay/headless-sdk/stripe';
39
+ *
40
+ * function ExpressSection({ sessionId, totals }) {
41
+ * const { containerRef, isReady } = useStripeExpress({
42
+ * checkoutSessionId: sessionId,
43
+ * amount: totals.total,
44
+ * currency: totals.currency,
45
+ * onSuccess: () => router.push('/confirmation'),
46
+ * onError: (msg) => toast.error(msg),
47
+ * });
48
+ *
49
+ * return <div ref={containerRef} style={{ display: isReady ? 'block' : 'none' }} />;
50
+ * }
51
+ * ```
52
+ */
53
+ export declare function useStripeExpress(opts: UseStripeExpressOptions): UseStripeExpressReturn;
54
+ //# sourceMappingURL=useStripeExpress.d.ts.map
@@ -0,0 +1,126 @@
1
+ import { useState, useRef, useEffect, useCallback } from 'react';
2
+ import { useHeadlessClient } from '../react/provider.js';
3
+ import { StripeExpressCheckout } from './StripeExpressCheckout.js';
4
+ /**
5
+ * React hook that mounts the Stripe Express Checkout Element into a ref'd container.
6
+ *
7
+ * Handles the full lifecycle: discover config -> load Stripe -> mount ECE ->
8
+ * onConfirm -> processStripeExpress -> stripe.confirmPayment -> poll -> callbacks.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * import { useStripeExpress } from '@tagadapay/headless-sdk/stripe';
13
+ *
14
+ * function ExpressSection({ sessionId, totals }) {
15
+ * const { containerRef, isReady } = useStripeExpress({
16
+ * checkoutSessionId: sessionId,
17
+ * amount: totals.total,
18
+ * currency: totals.currency,
19
+ * onSuccess: () => router.push('/confirmation'),
20
+ * onError: (msg) => toast.error(msg),
21
+ * });
22
+ *
23
+ * return <div ref={containerRef} style={{ display: isReady ? 'block' : 'none' }} />;
24
+ * }
25
+ * ```
26
+ */
27
+ export function useStripeExpress(opts) {
28
+ const client = useHeadlessClient();
29
+ const containerRef = useRef(null);
30
+ const eceRef = useRef(null);
31
+ const [isReady, setIsReady] = useState(false);
32
+ const [isProcessing, setIsProcessing] = useState(false);
33
+ const [expressMethods, setExpressMethods] = useState(null);
34
+ const callbacksRef = useRef(opts);
35
+ callbacksRef.current = opts;
36
+ const { checkoutSessionId, amount, currency, paymentSetupConfig } = opts;
37
+ useEffect(() => {
38
+ if (!checkoutSessionId || !containerRef.current)
39
+ return;
40
+ let mounted = true;
41
+ let instance = null;
42
+ const init = async () => {
43
+ const express = await client.payment.getExpressMethods(checkoutSessionId, paymentSetupConfig ? { paymentSetupConfig } : undefined);
44
+ if (!mounted)
45
+ return;
46
+ setExpressMethods(express);
47
+ const stripeMethod = findStripeExpressMethod(express);
48
+ if (!stripeMethod?.publishableKey || !stripeMethod?.processorId)
49
+ return;
50
+ instance = new StripeExpressCheckout({
51
+ client,
52
+ publishableKey: stripeMethod.publishableKey,
53
+ processorId: stripeMethod.processorId,
54
+ enabledMethods: stripeMethod.enabledMethods,
55
+ });
56
+ instance.on('ready', () => {
57
+ if (mounted)
58
+ setIsReady(true);
59
+ });
60
+ instance.on('success', (result) => {
61
+ if (mounted)
62
+ setIsProcessing(false);
63
+ callbacksRef.current.onSuccess?.(result);
64
+ });
65
+ instance.on('error', (msg) => {
66
+ if (mounted)
67
+ setIsProcessing(false);
68
+ callbacksRef.current.onError?.(msg);
69
+ });
70
+ instance.on('cancel', () => {
71
+ if (mounted)
72
+ setIsProcessing(false);
73
+ callbacksRef.current.onCancel?.();
74
+ });
75
+ eceRef.current = instance;
76
+ if (containerRef.current) {
77
+ await instance.mount(containerRef.current, {
78
+ checkoutSessionId,
79
+ amount,
80
+ currency,
81
+ });
82
+ }
83
+ };
84
+ void init();
85
+ return () => {
86
+ mounted = false;
87
+ instance?.unmount();
88
+ eceRef.current = null;
89
+ setIsReady(false);
90
+ };
91
+ }, [client, checkoutSessionId, paymentSetupConfig]); // eslint-disable-line react-hooks/exhaustive-deps
92
+ useEffect(() => {
93
+ eceRef.current?.updateAmount(amount, currency);
94
+ }, [amount, currency]);
95
+ const updateAmount = useCallback((newAmount, newCurrency) => {
96
+ eceRef.current?.updateAmount(newAmount, newCurrency);
97
+ }, []);
98
+ return { containerRef, isReady, isProcessing, expressMethods, updateAmount };
99
+ }
100
+ /**
101
+ * Find a Stripe ECE-capable method from the express methods map.
102
+ * Returns publishableKey, processorId, and list of enabled sub-methods.
103
+ */
104
+ function findStripeExpressMethod(express) {
105
+ const allMethods = [
106
+ { key: 'apple_pay', entry: express.applePay },
107
+ { key: 'google_pay', entry: express.googlePay },
108
+ { key: 'paypal', entry: express.paypal },
109
+ { key: 'link', entry: express.link },
110
+ { key: 'klarna_express', entry: express.klarna },
111
+ ];
112
+ for (const { entry } of allMethods) {
113
+ if (entry?.publishableKey && entry.processorId) {
114
+ const enabledMethods = allMethods
115
+ .filter(({ entry: e }) => e?.available && e.publishableKey === entry.publishableKey)
116
+ .map(({ key }) => key);
117
+ return {
118
+ publishableKey: entry.publishableKey,
119
+ processorId: entry.processorId,
120
+ enabledMethods,
121
+ };
122
+ }
123
+ }
124
+ return null;
125
+ }
126
+ //# sourceMappingURL=useStripeExpress.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tagadapay/headless-sdk",
3
- "version": "1.8.0-rc.0",
3
+ "version": "1.8.0",
4
4
  "description": "Headless SDK for using TagadaPay as a checkout, offers, and CRM backend from any external website",
5
5
  "type": "module",
6
6
  "main": "dist/core/index.js",
@@ -58,8 +58,7 @@
58
58
  }
59
59
  },
60
60
  "files": [
61
- "dist/index.js",
62
- "dist/index.d.ts",
61
+ "dist/**/*",
63
62
  "README.md"
64
63
  ],
65
64
  "repository": {