@glomopay/react-native-sdk 3.0.1 → 3.1.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.
- package/CHANGELOG.md +15 -0
- package/MIGRATION.md +50 -0
- package/README.md +131 -50
- package/lib/glomo-checkout.d.ts.map +1 -1
- package/lib/glomo-checkout.js +38 -1
- package/lib/glomo-standard-checkout.d.ts +20 -0
- package/lib/glomo-standard-checkout.d.ts.map +1 -1
- package/lib/glomo-standard-checkout.js +14 -3
- package/lib/glomo-subscriptions-checkout.d.ts +8 -0
- package/lib/glomo-subscriptions-checkout.d.ts.map +1 -0
- package/lib/glomo-subscriptions-checkout.js +85 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/types/checkout.d.ts +15 -3
- package/lib/types/checkout.d.ts.map +1 -1
- package/lib/types/subscriptions-checkout.d.ts +29 -0
- package/lib/types/subscriptions-checkout.d.ts.map +1 -0
- package/lib/types/subscriptions-checkout.js +3 -0
- package/lib/use-glomo-checkout.d.ts +34 -4
- package/lib/use-glomo-checkout.d.ts.map +1 -1
- package/lib/use-glomo-checkout.js +92 -13
- package/lib/use-standard-checkout.d.ts +11 -2
- package/lib/use-standard-checkout.d.ts.map +1 -1
- package/lib/use-standard-checkout.js +52 -35
- package/lib/utils/analytics.d.ts +87 -2
- package/lib/utils/analytics.d.ts.map +1 -1
- package/lib/utils/analytics.js +356 -15
- package/lib/utils/device-compliance.d.ts.map +1 -1
- package/lib/utils/device-compliance.js +0 -1
- package/package.json +15 -4
- package/src/glomo-checkout.tsx +79 -5
- package/src/glomo-standard-checkout.tsx +33 -4
- package/src/glomo-subscriptions-checkout.tsx +83 -0
- package/src/index.ts +1 -1
- package/src/types/checkout.ts +10 -3
- package/src/types/subscriptions-checkout.ts +31 -0
- package/src/use-glomo-checkout.tsx +158 -17
- package/src/use-standard-checkout.tsx +70 -52
- package/src/utils/analytics.ts +482 -17
- package/src/utils/device-compliance.ts +0 -1
|
@@ -6,8 +6,19 @@ import { WebView, type WebViewMessageEvent, type WebViewProps } from "react-nati
|
|
|
6
6
|
|
|
7
7
|
import { useStandardCheckout } from "./use-standard-checkout";
|
|
8
8
|
import { type GlomoStandardCheckoutRef, type GlomoStandardCheckoutProps } from "./types/standard-checkout";
|
|
9
|
+
import { type CheckoutAnalyticsTrackers } from "./utils/analytics";
|
|
9
10
|
import { isValidUrl } from "./utils/validation";
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Internal-only props for GlomoStandardCheckout.
|
|
14
|
+
* Used by GlomoSubscriptionsCheckout to inject subscription analytics trackers.
|
|
15
|
+
* Not exported to merchants via index.ts.
|
|
16
|
+
*/
|
|
17
|
+
export interface GlomoStandardCheckoutInternalProps extends GlomoStandardCheckoutProps {
|
|
18
|
+
_analyticsTrackers?: CheckoutAnalyticsTrackers;
|
|
19
|
+
_skipOrderIdValidation?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
11
22
|
/**
|
|
12
23
|
* The Main GlomoPay Standard Checkout Component
|
|
13
24
|
*
|
|
@@ -40,7 +51,9 @@ function GlomoStandardCheckoutComponent(
|
|
|
40
51
|
onPayViaBankBankConnectionSuccessful,
|
|
41
52
|
onUserRefusedCameraPermissions,
|
|
42
53
|
devMode = false,
|
|
43
|
-
|
|
54
|
+
_analyticsTrackers,
|
|
55
|
+
_skipOrderIdValidation,
|
|
56
|
+
}: GlomoStandardCheckoutInternalProps,
|
|
44
57
|
ref: React.ForwardedRef<GlomoStandardCheckoutRef>
|
|
45
58
|
) {
|
|
46
59
|
// Computing mockMode based on the publicKey prefix
|
|
@@ -89,7 +102,8 @@ function GlomoStandardCheckoutComponent(
|
|
|
89
102
|
onUserRefusedCameraPermissions,
|
|
90
103
|
devMode,
|
|
91
104
|
},
|
|
92
|
-
mockMode
|
|
105
|
+
mockMode,
|
|
106
|
+
{ analyticsTrackers: _analyticsTrackers, _skipOrderIdValidation }
|
|
93
107
|
);
|
|
94
108
|
|
|
95
109
|
// Exposing the start() and getStatus() methods via the ref
|
|
@@ -262,11 +276,26 @@ function GlomoStandardCheckoutComponent(
|
|
|
262
276
|
);
|
|
263
277
|
}
|
|
264
278
|
|
|
265
|
-
|
|
279
|
+
/**
|
|
280
|
+
* Widening the internal type to GlomoStandardCheckoutInternalProps for internal SDK use,
|
|
281
|
+
* then narrowing the public export to GlomoStandardCheckoutProps so merchants cannot
|
|
282
|
+
* pass internal-only props like _analyticsTrackers.
|
|
283
|
+
*/
|
|
284
|
+
export const GlomoStandardCheckout = forwardRef(GlomoStandardCheckoutComponent) as React.ForwardRefExoticComponent<
|
|
285
|
+
GlomoStandardCheckoutProps & React.RefAttributes<GlomoStandardCheckoutRef>
|
|
286
|
+
>;
|
|
287
|
+
|
|
288
|
+
GlomoStandardCheckout.displayName = "GlomoStandardCheckout";
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Internal reference - accepts GlomoStandardCheckoutInternalProps.
|
|
292
|
+
* Not re-exported from index.ts.
|
|
293
|
+
*/
|
|
294
|
+
export const GlomoStandardCheckoutInternal = forwardRef<GlomoStandardCheckoutRef, GlomoStandardCheckoutInternalProps>(
|
|
266
295
|
GlomoStandardCheckoutComponent
|
|
267
296
|
);
|
|
268
297
|
|
|
269
|
-
|
|
298
|
+
GlomoStandardCheckoutInternal.displayName = "GlomoStandardCheckoutInternal";
|
|
270
299
|
|
|
271
300
|
const styles = StyleSheet.create({
|
|
272
301
|
modalContainer: {
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GlomoSubscriptionsCheckout - thin wrapper around GlomoStandardCheckout
|
|
3
|
+
* that accepts a subscriptionId (sub_*) instead of an orderId (order_*).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useRef, useImperativeHandle, forwardRef, useEffect } from "react";
|
|
7
|
+
|
|
8
|
+
import { GlomoStandardCheckoutInternal } from "./glomo-standard-checkout";
|
|
9
|
+
import { type GlomoStandardCheckoutRef } from "./types/standard-checkout";
|
|
10
|
+
import {
|
|
11
|
+
type GlomoSubscriptionsCheckoutProps,
|
|
12
|
+
type GlomoSubscriptionsCheckoutRef,
|
|
13
|
+
} from "./types/subscriptions-checkout";
|
|
14
|
+
import { subscriptionCheckoutTrackers } from "./utils/analytics";
|
|
15
|
+
import { safeCallback } from "./utils/validation";
|
|
16
|
+
|
|
17
|
+
function GlomoSubscriptionsCheckoutComponent(
|
|
18
|
+
props: GlomoSubscriptionsCheckoutProps,
|
|
19
|
+
ref: React.ForwardedRef<GlomoSubscriptionsCheckoutRef>
|
|
20
|
+
) {
|
|
21
|
+
const { subscriptionId: rawSubscriptionId, onSdkError, devMode = false, ...rest } = props;
|
|
22
|
+
const innerRef = useRef<GlomoStandardCheckoutRef>(null);
|
|
23
|
+
const onSdkErrorRef = useRef(onSdkError);
|
|
24
|
+
onSdkErrorRef.current = onSdkError;
|
|
25
|
+
|
|
26
|
+
const subscriptionId = rawSubscriptionId.trim();
|
|
27
|
+
|
|
28
|
+
/** Validating subscriptionId - firing onSdkError via useEffect to avoid side effects during render */
|
|
29
|
+
const validationError = (() => {
|
|
30
|
+
if (subscriptionId.length === 0) {
|
|
31
|
+
return "subscriptionId must not be empty";
|
|
32
|
+
}
|
|
33
|
+
if (!subscriptionId.startsWith("sub_")) {
|
|
34
|
+
return "subscriptionId must start with 'sub_'";
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
})();
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (validationError) {
|
|
41
|
+
safeCallback(
|
|
42
|
+
onSdkErrorRef.current,
|
|
43
|
+
[[{ type: "validation_error", message: validationError }]],
|
|
44
|
+
"onSdkError",
|
|
45
|
+
devMode
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}, [validationError, devMode]);
|
|
49
|
+
|
|
50
|
+
useImperativeHandle(ref, () => ({
|
|
51
|
+
start: () => {
|
|
52
|
+
if (validationError) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return innerRef.current?.start() ?? false;
|
|
56
|
+
},
|
|
57
|
+
getStatus: () => {
|
|
58
|
+
return innerRef.current?.getStatus() ?? "ready";
|
|
59
|
+
},
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
if (validationError) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<GlomoStandardCheckoutInternal
|
|
68
|
+
ref={innerRef}
|
|
69
|
+
{...rest}
|
|
70
|
+
orderId={subscriptionId}
|
|
71
|
+
onSdkError={onSdkError}
|
|
72
|
+
devMode={devMode}
|
|
73
|
+
_analyticsTrackers={subscriptionCheckoutTrackers}
|
|
74
|
+
_skipOrderIdValidation={true}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const GlomoSubscriptionsCheckout = forwardRef<GlomoSubscriptionsCheckoutRef, GlomoSubscriptionsCheckoutProps>(
|
|
80
|
+
GlomoSubscriptionsCheckoutComponent
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
GlomoSubscriptionsCheckout.displayName = "GlomoSubscriptionsCheckout";
|
package/src/index.ts
CHANGED
|
@@ -12,4 +12,4 @@ export {
|
|
|
12
12
|
} from "./types/checkout";
|
|
13
13
|
export { type GlomoServer } from "./config/base";
|
|
14
14
|
export { type SdkError } from "./utils/analytics";
|
|
15
|
-
export { useGlomoCheckout } from "./use-glomo-checkout";
|
|
15
|
+
export { useGlomoCheckout, type UseGlomoCheckoutReturn } from "./use-glomo-checkout";
|
package/src/types/checkout.ts
CHANGED
|
@@ -47,11 +47,10 @@ export interface GlomoCheckoutRef {
|
|
|
47
47
|
getStatus: () => CheckoutStatus;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
/**
|
|
51
|
-
|
|
50
|
+
/** Shared callback and configuration props for the unified checkout component */
|
|
51
|
+
interface GlomoCheckoutBaseProps {
|
|
52
52
|
server?: GlomoServer;
|
|
53
53
|
publicKey: string;
|
|
54
|
-
orderId: string;
|
|
55
54
|
onPaymentSuccess: (payload: GlomoCheckoutPayload) => void;
|
|
56
55
|
onPaymentFailure: (payload: GlomoCheckoutPayload) => void;
|
|
57
56
|
onConnectionError?: (error: unknown) => void;
|
|
@@ -63,3 +62,11 @@ export interface GlomoCheckoutProps {
|
|
|
63
62
|
onUserRefusedCameraPermissions?: () => void;
|
|
64
63
|
devMode?: boolean;
|
|
65
64
|
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Props for the unified checkout component.
|
|
68
|
+
* Exactly one of orderId or subscriptionId must be provided.
|
|
69
|
+
* TypeScript enforces this at compile time; a runtime guard in useGlomoCheckout catches JS consumers.
|
|
70
|
+
*/
|
|
71
|
+
export type GlomoCheckoutProps = GlomoCheckoutBaseProps &
|
|
72
|
+
({ orderId: string; subscriptionId?: never } | { subscriptionId: string; orderId?: never });
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Type definitions for the Subscriptions Checkout flow */
|
|
2
|
+
|
|
3
|
+
import { type GlomoServer } from "../config/base";
|
|
4
|
+
import { type SdkError } from "../utils/analytics";
|
|
5
|
+
import { type GlomoCheckoutPayload, type GlomoPayViaBankConnectionPayload } from "./checkout";
|
|
6
|
+
import { type StandardCheckoutStatus } from "./standard-checkout";
|
|
7
|
+
|
|
8
|
+
/** Props for the subscriptions checkout component */
|
|
9
|
+
export interface GlomoSubscriptionsCheckoutProps {
|
|
10
|
+
server?: GlomoServer;
|
|
11
|
+
publicKey: string;
|
|
12
|
+
subscriptionId: string;
|
|
13
|
+
onPaymentSuccess: (payload: GlomoCheckoutPayload) => void;
|
|
14
|
+
onPaymentFailure: (payload: GlomoCheckoutPayload) => void;
|
|
15
|
+
onConnectionError?: (error: unknown) => void;
|
|
16
|
+
onPaymentTerminate?: () => void;
|
|
17
|
+
onSdkError?: (error: Array<SdkError>) => void;
|
|
18
|
+
onPayViaBankBankConnectionSuccessful?: (payload: GlomoPayViaBankConnectionPayload | null | undefined) => void;
|
|
19
|
+
onUserRefusedCameraPermissions?: () => void;
|
|
20
|
+
devMode?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Ref handle for the subscriptions checkout component.
|
|
25
|
+
* Same type as GlomoStandardCheckoutRef - start() is async at the unified level
|
|
26
|
+
* but delegates to the synchronous standard checkout start() internally.
|
|
27
|
+
*/
|
|
28
|
+
export interface GlomoSubscriptionsCheckoutRef {
|
|
29
|
+
start: () => boolean;
|
|
30
|
+
getStatus: () => StandardCheckoutStatus;
|
|
31
|
+
}
|
|
@@ -8,6 +8,7 @@ import { useState, useRef, useCallback, useEffect } from "react";
|
|
|
8
8
|
import { type GlomoCheckoutProps, type CheckoutStatus } from "./types/checkout";
|
|
9
9
|
import { type GlomoLrsCheckoutRef } from "./glomo-lrs-checkout";
|
|
10
10
|
import { type GlomoStandardCheckoutRef } from "./types/standard-checkout";
|
|
11
|
+
import { type GlomoSubscriptionsCheckoutRef } from "./types/subscriptions-checkout";
|
|
11
12
|
import { type OrderType, fetchOrderType } from "./services/order-type-fetcher";
|
|
12
13
|
import { isValidPublicKey, isValidOrderId, safeCallback } from "./utils/validation";
|
|
13
14
|
import {
|
|
@@ -15,34 +16,126 @@ import {
|
|
|
15
16
|
trackOrderTypeDetectionStarted,
|
|
16
17
|
trackOrderTypeDetectionResolved,
|
|
17
18
|
trackOrderTypeDetectionFailed,
|
|
19
|
+
trackUseOfUnsupportedFunctionality,
|
|
18
20
|
} from "./utils/analytics";
|
|
19
21
|
|
|
20
|
-
/**
|
|
22
|
+
/** Extended order type including subscriptions */
|
|
23
|
+
type ExtendedOrderType = OrderType | "subscriptions";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Public return type for useGlomoCheckout.
|
|
27
|
+
* Exposes only start() and getStatus() - no internal refs or state.
|
|
28
|
+
*/
|
|
21
29
|
export interface UseGlomoCheckoutReturn {
|
|
22
|
-
|
|
30
|
+
start: () => Promise<boolean>;
|
|
31
|
+
getStatus: () => CheckoutStatus;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Internal return type - used by GlomoCheckout component.
|
|
36
|
+
* Not exported from the public API.
|
|
37
|
+
*/
|
|
38
|
+
export interface UseGlomoCheckoutInternalReturn {
|
|
39
|
+
orderType: ExtendedOrderType | null;
|
|
23
40
|
detecting: boolean;
|
|
24
41
|
start: () => Promise<boolean>;
|
|
25
42
|
getStatus: () => CheckoutStatus;
|
|
26
43
|
innerLrsRef: React.RefObject<GlomoLrsCheckoutRef>;
|
|
27
44
|
innerStandardRef: React.RefObject<GlomoStandardCheckoutRef>;
|
|
45
|
+
innerSubscriptionsRef: React.RefObject<GlomoSubscriptionsCheckoutRef>;
|
|
28
46
|
props: GlomoCheckoutProps;
|
|
29
47
|
}
|
|
30
48
|
|
|
31
49
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
50
|
+
* @deprecated Legacy hook - will be removed in a future major version.
|
|
51
|
+
*
|
|
52
|
+
* This hook does not provide reactive state. `getStatus()` returns a
|
|
53
|
+
* point-in-time snapshot and does not trigger re-renders on status changes.
|
|
54
|
+
* `start()` returns a promise identical to `GlomoCheckoutRef.start()`.
|
|
55
|
+
*
|
|
56
|
+
* Use the `GlomoCheckout` component with a ref instead:
|
|
57
|
+
*
|
|
58
|
+
* ```tsx
|
|
59
|
+
* const ref = useRef<GlomoCheckoutRef>(null);
|
|
60
|
+
* const started = await ref.current?.start();
|
|
61
|
+
* const status = ref.current?.getStatus();
|
|
62
|
+
* ```
|
|
34
63
|
*/
|
|
35
|
-
export function useGlomoCheckout(props: GlomoCheckoutProps): UseGlomoCheckoutReturn
|
|
36
|
-
|
|
64
|
+
export function useGlomoCheckout(props: GlomoCheckoutProps): UseGlomoCheckoutReturn;
|
|
65
|
+
export function useGlomoCheckout(
|
|
66
|
+
props: GlomoCheckoutProps,
|
|
67
|
+
internalOptions: { _suppressDeprecationWarning: true }
|
|
68
|
+
): UseGlomoCheckoutInternalReturn;
|
|
69
|
+
export function useGlomoCheckout(
|
|
70
|
+
props: GlomoCheckoutProps,
|
|
71
|
+
internalOptions?: { _suppressDeprecationWarning?: boolean }
|
|
72
|
+
): UseGlomoCheckoutInternalReturn {
|
|
73
|
+
const { server, publicKey, orderId, subscriptionId, onSdkError, devMode = false } = props;
|
|
37
74
|
|
|
38
75
|
// Order type resolved from the API - null until detection completes
|
|
39
|
-
const [orderType, setOrderType] = useState<
|
|
76
|
+
const [orderType, setOrderType] = useState<ExtendedOrderType | null>(null);
|
|
40
77
|
// Whether order type detection is in progress
|
|
41
78
|
const [detecting, setDetecting] = useState(false);
|
|
79
|
+
// Whether the props have a mutual exclusion error
|
|
80
|
+
const [propsError, setPropsError] = useState(false);
|
|
42
81
|
|
|
43
|
-
//
|
|
82
|
+
// Stabilizing onSdkError ref to avoid re-running effects on inline callback changes
|
|
83
|
+
const onSdkErrorRef = useRef(onSdkError);
|
|
84
|
+
onSdkErrorRef.current = onSdkError;
|
|
85
|
+
|
|
86
|
+
// One-time deprecation warning for direct merchant usage
|
|
87
|
+
const suppressWarning = internalOptions?._suppressDeprecationWarning ?? false;
|
|
88
|
+
const mockMode = publicKey?.toLowerCase().startsWith("test_") ?? false;
|
|
89
|
+
const hasWarnedRef = useRef(false);
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!hasWarnedRef.current && !suppressWarning) {
|
|
92
|
+
hasWarnedRef.current = true;
|
|
93
|
+
console.warn(
|
|
94
|
+
"[Glomo-RN-SDK] useGlomoCheckout is deprecated and will be removed in a future major version. " +
|
|
95
|
+
"Use the GlomoCheckout component with a ref instead. " +
|
|
96
|
+
"This hook does not provide reactive state - getStatus() is a point-in-time snapshot, not a reactive value."
|
|
97
|
+
);
|
|
98
|
+
trackUseOfUnsupportedFunctionality(
|
|
99
|
+
"useGlomoCheckout",
|
|
100
|
+
orderId ?? subscriptionId,
|
|
101
|
+
publicKey,
|
|
102
|
+
devMode,
|
|
103
|
+
mockMode,
|
|
104
|
+
""
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}, [suppressWarning, orderId, subscriptionId, publicKey, devMode, mockMode]);
|
|
108
|
+
|
|
109
|
+
// Refs for the inner LRS, Standard, and Subscriptions checkout components
|
|
44
110
|
const innerLrsRef = useRef<GlomoLrsCheckoutRef>(null);
|
|
45
111
|
const innerStandardRef = useRef<GlomoStandardCheckoutRef>(null);
|
|
112
|
+
const innerSubscriptionsRef = useRef<GlomoSubscriptionsCheckoutRef>(null);
|
|
113
|
+
|
|
114
|
+
/** Mutual exclusion guard - firing onSdkError if both or neither id is provided */
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
const hasBoth = Boolean(orderId) && Boolean(subscriptionId);
|
|
117
|
+
const hasNeither = !orderId && !subscriptionId;
|
|
118
|
+
|
|
119
|
+
if (hasBoth) {
|
|
120
|
+
setPropsError(true);
|
|
121
|
+
safeCallback(
|
|
122
|
+
onSdkErrorRef.current,
|
|
123
|
+
[[{ type: "validation_error", message: "Provide either orderId or subscriptionId, not both" }]],
|
|
124
|
+
"onSdkError",
|
|
125
|
+
devMode
|
|
126
|
+
);
|
|
127
|
+
} else if (hasNeither) {
|
|
128
|
+
setPropsError(true);
|
|
129
|
+
safeCallback(
|
|
130
|
+
onSdkErrorRef.current,
|
|
131
|
+
[[{ type: "validation_error", message: "Provide orderId or subscriptionId" }]],
|
|
132
|
+
"onSdkError",
|
|
133
|
+
devMode
|
|
134
|
+
);
|
|
135
|
+
} else {
|
|
136
|
+
setPropsError(false);
|
|
137
|
+
}
|
|
138
|
+
}, [orderId, subscriptionId, devMode]);
|
|
46
139
|
|
|
47
140
|
/**
|
|
48
141
|
* Tracking whether start() has been called and the inner component needs to be auto-started.
|
|
@@ -76,6 +169,9 @@ export function useGlomoCheckout(props: GlomoCheckoutProps): UseGlomoCheckoutRet
|
|
|
76
169
|
if (orderType === "standard" && innerStandardRef.current) {
|
|
77
170
|
return innerStandardRef.current.getStatus();
|
|
78
171
|
}
|
|
172
|
+
if (orderType === "subscriptions" && innerSubscriptionsRef.current) {
|
|
173
|
+
return innerSubscriptionsRef.current.getStatus();
|
|
174
|
+
}
|
|
79
175
|
return "ready";
|
|
80
176
|
}, [detecting, orderType]);
|
|
81
177
|
|
|
@@ -94,6 +190,14 @@ export function useGlomoCheckout(props: GlomoCheckoutProps): UseGlomoCheckoutRet
|
|
|
94
190
|
console.log("[Glomo-RN-SDK] start() called");
|
|
95
191
|
}
|
|
96
192
|
|
|
193
|
+
// Aborting if mutual exclusion guard detected a props error
|
|
194
|
+
if (propsError) {
|
|
195
|
+
if (devMode) {
|
|
196
|
+
console.error("[Glomo-RN-SDK] start() aborted: invalid orderId/subscriptionId combination");
|
|
197
|
+
}
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
|
|
97
201
|
// Validating inputs before making the API call
|
|
98
202
|
const errors: SdkError[] = [];
|
|
99
203
|
|
|
@@ -105,7 +209,11 @@ export function useGlomoCheckout(props: GlomoCheckoutProps): UseGlomoCheckoutRet
|
|
|
105
209
|
});
|
|
106
210
|
}
|
|
107
211
|
|
|
108
|
-
|
|
212
|
+
/**
|
|
213
|
+
* For subscriptions flow, skipping orderId validation entirely - GlomoSubscriptionsCheckout
|
|
214
|
+
* owns subscriptionId validation (trim, empty, prefix). For order flow, validating orderId.
|
|
215
|
+
*/
|
|
216
|
+
if (!subscriptionId && !isValidOrderId(orderId!)) {
|
|
109
217
|
errors.push({
|
|
110
218
|
type: "validation_error",
|
|
111
219
|
message: "Invalid orderId: must start with 'order_' and be a valid string",
|
|
@@ -122,6 +230,24 @@ export function useGlomoCheckout(props: GlomoCheckoutProps): UseGlomoCheckoutRet
|
|
|
122
230
|
return false;
|
|
123
231
|
}
|
|
124
232
|
|
|
233
|
+
/**
|
|
234
|
+
* For subscriptions, orderType is already "subscriptions" and the inner component is mounted.
|
|
235
|
+
* Calling start() directly - no promise/effect bridge needed since no async detection occurs.
|
|
236
|
+
*/
|
|
237
|
+
if (subscriptionId) {
|
|
238
|
+
if (innerSubscriptionsRef.current) {
|
|
239
|
+
const started = innerSubscriptionsRef.current.start();
|
|
240
|
+
if (devMode) {
|
|
241
|
+
console.log(`[Glomo-RN-SDK] Subscriptions inner start() returned: ${started}`);
|
|
242
|
+
}
|
|
243
|
+
return started;
|
|
244
|
+
}
|
|
245
|
+
if (devMode) {
|
|
246
|
+
console.error("[Glomo-RN-SDK] Subscriptions component not mounted");
|
|
247
|
+
}
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
|
|
125
251
|
// Detecting order type via the API
|
|
126
252
|
setDetecting(true);
|
|
127
253
|
const thisGeneration = ++fetchGenerationRef.current;
|
|
@@ -130,9 +256,9 @@ export function useGlomoCheckout(props: GlomoCheckoutProps): UseGlomoCheckoutRet
|
|
|
130
256
|
console.log("[Glomo-RN-SDK] Detecting order type...");
|
|
131
257
|
}
|
|
132
258
|
|
|
133
|
-
trackOrderTypeDetectionStarted(orderId
|
|
259
|
+
trackOrderTypeDetectionStarted(orderId!, publicKey, devMode);
|
|
134
260
|
|
|
135
|
-
const result = await fetchOrderType({ orderId
|
|
261
|
+
const result = await fetchOrderType({ orderId: orderId!, publicKey, server, devMode });
|
|
136
262
|
|
|
137
263
|
// Discarding stale result if props changed while the fetch was in flight
|
|
138
264
|
if (thisGeneration !== fetchGenerationRef.current) {
|
|
@@ -149,7 +275,7 @@ export function useGlomoCheckout(props: GlomoCheckoutProps): UseGlomoCheckoutRet
|
|
|
149
275
|
if (devMode) {
|
|
150
276
|
console.error(`[Glomo-RN-SDK] Order type detection failed: ${result.error}`);
|
|
151
277
|
}
|
|
152
|
-
trackOrderTypeDetectionFailed(orderId
|
|
278
|
+
trackOrderTypeDetectionFailed(orderId!, publicKey, result.error, devMode);
|
|
153
279
|
safeCallback(onSdkError, [[{ type: "validation_error", message: result.error }]], "onSdkError", devMode);
|
|
154
280
|
return false;
|
|
155
281
|
}
|
|
@@ -158,7 +284,7 @@ export function useGlomoCheckout(props: GlomoCheckoutProps): UseGlomoCheckoutRet
|
|
|
158
284
|
console.log(`[Glomo-RN-SDK] Order type resolved: ${result.orderType}`);
|
|
159
285
|
}
|
|
160
286
|
|
|
161
|
-
trackOrderTypeDetectionResolved(orderId
|
|
287
|
+
trackOrderTypeDetectionResolved(orderId!, publicKey, result.orderType, devMode);
|
|
162
288
|
|
|
163
289
|
// Setting the resolved order type - this triggers a re-render that mounts the inner component
|
|
164
290
|
setOrderType(result.orderType);
|
|
@@ -168,7 +294,7 @@ export function useGlomoCheckout(props: GlomoCheckoutProps): UseGlomoCheckoutRet
|
|
|
168
294
|
pendingStartResolveRef.current = resolve;
|
|
169
295
|
pendingStartRef.current = true;
|
|
170
296
|
});
|
|
171
|
-
}, [devMode, publicKey, orderId, server, onSdkError]);
|
|
297
|
+
}, [devMode, publicKey, orderId, subscriptionId, server, onSdkError, propsError]);
|
|
172
298
|
|
|
173
299
|
/**
|
|
174
300
|
* Auto-starting the inner component after order type is resolved and the component has rendered.
|
|
@@ -194,6 +320,10 @@ export function useGlomoCheckout(props: GlomoCheckoutProps): UseGlomoCheckoutRet
|
|
|
194
320
|
if (devMode) {
|
|
195
321
|
console.log(`[Glomo-RN-SDK] Standard inner start() returned: ${started}`);
|
|
196
322
|
}
|
|
323
|
+
} else if (orderType === "subscriptions") {
|
|
324
|
+
// No-op: subscription start() calls the inner component directly (not via this effect bridge).
|
|
325
|
+
// If we reach here, pendingStartRef was set unexpectedly - resolve false defensively.
|
|
326
|
+
pendingStartRef.current = false;
|
|
197
327
|
}
|
|
198
328
|
|
|
199
329
|
// Resolving the merchant's awaited start() promise with the inner component's result
|
|
@@ -203,7 +333,12 @@ export function useGlomoCheckout(props: GlomoCheckoutProps): UseGlomoCheckoutRet
|
|
|
203
333
|
}
|
|
204
334
|
}, [orderType, devMode]);
|
|
205
335
|
|
|
206
|
-
|
|
336
|
+
/**
|
|
337
|
+
* Resetting order type state when orderId, subscriptionId, or publicKey changes.
|
|
338
|
+
* For subscriptions, setting orderType to "subscriptions" directly (no API call needed).
|
|
339
|
+
* For orders, resetting to null to force re-detection on next start().
|
|
340
|
+
* Single effect owns all orderType transitions to avoid race conditions.
|
|
341
|
+
*/
|
|
207
342
|
useEffect(() => {
|
|
208
343
|
// Invalidating any in-flight fetchOrderType call so its stale result is discarded
|
|
209
344
|
fetchGenerationRef.current++;
|
|
@@ -211,10 +346,15 @@ export function useGlomoCheckout(props: GlomoCheckoutProps): UseGlomoCheckoutRet
|
|
|
211
346
|
pendingStartResolveRef.current(false);
|
|
212
347
|
pendingStartResolveRef.current = null;
|
|
213
348
|
}
|
|
214
|
-
setOrderType(null);
|
|
215
349
|
setDetecting(false);
|
|
216
350
|
pendingStartRef.current = false;
|
|
217
|
-
|
|
351
|
+
|
|
352
|
+
if (subscriptionId && !orderId) {
|
|
353
|
+
setOrderType("subscriptions");
|
|
354
|
+
} else {
|
|
355
|
+
setOrderType(null);
|
|
356
|
+
}
|
|
357
|
+
}, [orderId, subscriptionId, publicKey]);
|
|
218
358
|
|
|
219
359
|
return {
|
|
220
360
|
orderType,
|
|
@@ -223,6 +363,7 @@ export function useGlomoCheckout(props: GlomoCheckoutProps): UseGlomoCheckoutRet
|
|
|
223
363
|
getStatus,
|
|
224
364
|
innerLrsRef,
|
|
225
365
|
innerStandardRef,
|
|
366
|
+
innerSubscriptionsRef,
|
|
226
367
|
props,
|
|
227
368
|
};
|
|
228
369
|
}
|