@idealyst/payments 1.2.108 → 1.2.109
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 +54 -56
- package/package.json +11 -11
- package/src/constants.ts +3 -4
- package/src/errors.ts +53 -21
- package/src/index.native.ts +30 -18
- package/src/index.ts +30 -18
- package/src/index.web.ts +30 -18
- package/src/payments.native.ts +249 -192
- package/src/payments.web.ts +47 -68
- package/src/types.ts +136 -122
- package/src/usePayments.ts +149 -85
package/src/payments.web.ts
CHANGED
|
@@ -1,91 +1,70 @@
|
|
|
1
1
|
// ============================================================================
|
|
2
|
-
// Web
|
|
2
|
+
// Web IAP Stub
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// This stub initializes and reports availability so the hook works,
|
|
8
|
-
// but payment operations throw descriptive errors with guidance.
|
|
4
|
+
// In-App Purchases are a native-only concept (StoreKit / Google Play Billing).
|
|
5
|
+
// This stub allows the hook and types to be imported on web without errors,
|
|
6
|
+
// but all operations throw descriptive errors.
|
|
9
7
|
// ============================================================================
|
|
10
8
|
|
|
11
9
|
import type {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
IAPConfig,
|
|
11
|
+
IAPProviderStatus,
|
|
12
|
+
IAPProduct,
|
|
13
|
+
IAPSubscription,
|
|
14
|
+
IAPPurchase,
|
|
17
15
|
} from './types';
|
|
18
16
|
import { INITIAL_PROVIDER_STATUS } from './constants';
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
const WEB_NOT_SUPPORTED_MESSAGE =
|
|
22
|
-
'@idealyst/payments does not provide a web payment UI. ' +
|
|
23
|
-
'Use Stripe Elements (@stripe/react-stripe-js) or Stripe Checkout for web payments.';
|
|
17
|
+
import { createIAPError } from './errors';
|
|
24
18
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
isAvailable: false,
|
|
29
|
-
unavailableReason: 'Apple Pay requires native iOS integration',
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
type: 'google_pay',
|
|
33
|
-
isAvailable: false,
|
|
34
|
-
unavailableReason: 'Google Pay requires native Android integration',
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
type: 'card',
|
|
38
|
-
isAvailable: false,
|
|
39
|
-
unavailableReason:
|
|
40
|
-
'Use Stripe Elements (@stripe/react-stripe-js) for web card payments',
|
|
41
|
-
},
|
|
42
|
-
];
|
|
19
|
+
const NOT_SUPPORTED_MESSAGE =
|
|
20
|
+
'In-App Purchases are only available on native iOS and Android platforms. ' +
|
|
21
|
+
'For web payments, use a payment processor like Stripe directly.';
|
|
43
22
|
|
|
44
|
-
let _status:
|
|
23
|
+
let _status: IAPProviderStatus = { ...INITIAL_PROVIDER_STATUS };
|
|
45
24
|
|
|
46
|
-
|
|
47
|
-
* Get the current payment provider status.
|
|
48
|
-
*/
|
|
49
|
-
export function getPaymentStatus(): PaymentProviderStatus {
|
|
25
|
+
export function getIAPStatus(): IAPProviderStatus {
|
|
50
26
|
return { ..._status };
|
|
51
27
|
}
|
|
52
28
|
|
|
53
|
-
|
|
54
|
-
* Initialize — succeeds on web but reports all methods as unavailable.
|
|
55
|
-
*/
|
|
56
|
-
export async function initializePayments(
|
|
57
|
-
_config: PaymentConfig,
|
|
58
|
-
): Promise<void> {
|
|
29
|
+
export async function initializeIAP(_config?: IAPConfig): Promise<void> {
|
|
59
30
|
_status = {
|
|
60
31
|
state: 'ready',
|
|
61
|
-
|
|
62
|
-
isPaymentAvailable: false,
|
|
32
|
+
isStoreAvailable: false,
|
|
63
33
|
};
|
|
64
34
|
}
|
|
65
35
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
36
|
+
export async function getProducts(_skus: string[]): Promise<IAPProduct[]> {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function getSubscriptions(
|
|
41
|
+
_skus: string[],
|
|
42
|
+
): Promise<IAPSubscription[]> {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function purchaseProduct(_sku: string): Promise<IAPPurchase> {
|
|
47
|
+
throw createIAPError('not_supported', NOT_SUPPORTED_MESSAGE);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function purchaseSubscription(
|
|
51
|
+
_sku: string,
|
|
52
|
+
_offerToken?: string,
|
|
53
|
+
): Promise<IAPPurchase> {
|
|
54
|
+
throw createIAPError('not_supported', NOT_SUPPORTED_MESSAGE);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function finishTransaction(
|
|
58
|
+
_purchase: IAPPurchase,
|
|
59
|
+
_isConsumable?: boolean,
|
|
60
|
+
): Promise<void> {
|
|
61
|
+
throw createIAPError('not_supported', NOT_SUPPORTED_MESSAGE);
|
|
73
62
|
}
|
|
74
63
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
*/
|
|
78
|
-
export async function confirmPayment(
|
|
79
|
-
_request: PaymentSheetRequest,
|
|
80
|
-
): Promise<PaymentResult> {
|
|
81
|
-
throw createPaymentError('not_supported', WEB_NOT_SUPPORTED_MESSAGE);
|
|
64
|
+
export async function restorePurchases(): Promise<IAPPurchase[]> {
|
|
65
|
+
return [];
|
|
82
66
|
}
|
|
83
67
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
*/
|
|
87
|
-
export async function createPaymentMethod(
|
|
88
|
-
_request: PaymentSheetRequest,
|
|
89
|
-
): Promise<PaymentResult> {
|
|
90
|
-
throw createPaymentError('not_supported', WEB_NOT_SUPPORTED_MESSAGE);
|
|
68
|
+
export async function endConnection(): Promise<void> {
|
|
69
|
+
_status = { ...INITIAL_PROVIDER_STATUS };
|
|
91
70
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,179 +1,193 @@
|
|
|
1
1
|
// ============================================================================
|
|
2
|
-
//
|
|
2
|
+
// Product Types
|
|
3
3
|
// ============================================================================
|
|
4
4
|
|
|
5
|
-
export type
|
|
6
|
-
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
|
|
5
|
+
export type ProductType = 'iap' | 'sub';
|
|
6
|
+
|
|
7
|
+
export type ProductPlatform = 'ios' | 'android';
|
|
8
|
+
|
|
9
|
+
export interface IAPProduct {
|
|
10
|
+
/** Store-specific product identifier (SKU). */
|
|
11
|
+
sku: string;
|
|
12
|
+
/** Product title from the store. */
|
|
13
|
+
title: string;
|
|
14
|
+
/** Product description from the store. */
|
|
15
|
+
description: string;
|
|
16
|
+
/** Price as a number (e.g., 4.99). */
|
|
17
|
+
price: number;
|
|
18
|
+
/** Localized price string (e.g., "$4.99"). */
|
|
19
|
+
priceFormatted: string;
|
|
20
|
+
/** ISO 4217 currency code (e.g., "USD"). */
|
|
21
|
+
currency: string;
|
|
22
|
+
/** Product type: one-time purchase or subscription. */
|
|
23
|
+
type: ProductType;
|
|
24
|
+
/** Platform this product was fetched from. */
|
|
25
|
+
platform: ProductPlatform;
|
|
14
26
|
}
|
|
15
27
|
|
|
16
28
|
// ============================================================================
|
|
17
|
-
//
|
|
29
|
+
// Subscription Types
|
|
18
30
|
// ============================================================================
|
|
19
31
|
|
|
20
|
-
export
|
|
21
|
-
/** Stripe publishable key (pk_test_xxx or pk_live_xxx). */
|
|
22
|
-
publishableKey: string;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Apple Pay merchant identifier (e.g., "merchant.com.yourapp").
|
|
26
|
-
* Required for Apple Pay on iOS.
|
|
27
|
-
*/
|
|
28
|
-
merchantIdentifier?: string;
|
|
29
|
-
|
|
30
|
-
/** Merchant display name shown on the payment sheet. */
|
|
31
|
-
merchantName: string;
|
|
32
|
+
export type SubscriptionPeriodUnit = 'day' | 'week' | 'month' | 'year';
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
export interface SubscriptionPeriod {
|
|
35
|
+
/** Time unit. */
|
|
36
|
+
unit: SubscriptionPeriodUnit;
|
|
37
|
+
/** Number of units per period (e.g., 1 month, 3 months). */
|
|
38
|
+
numberOfUnits: number;
|
|
39
|
+
}
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
export type DiscountPaymentMode = 'freeTrial' | 'payAsYouGo' | 'payUpFront';
|
|
42
|
+
export type DiscountType = 'introductory' | 'promotional';
|
|
43
|
+
|
|
44
|
+
export interface SubscriptionDiscount {
|
|
45
|
+
/** Discount identifier (promotional only). */
|
|
46
|
+
identifier?: string;
|
|
47
|
+
/** Discounted price as a number. */
|
|
48
|
+
price: number;
|
|
49
|
+
/** Localized discounted price string. */
|
|
50
|
+
priceFormatted: string;
|
|
51
|
+
/** Discount billing period. */
|
|
52
|
+
period: SubscriptionPeriod;
|
|
53
|
+
/** How the discount is applied. */
|
|
54
|
+
paymentMode: DiscountPaymentMode;
|
|
55
|
+
/** Whether this is introductory or promotional. */
|
|
56
|
+
type: DiscountType;
|
|
57
|
+
}
|
|
41
58
|
|
|
42
|
-
|
|
43
|
-
|
|
59
|
+
export interface IAPSubscription extends IAPProduct {
|
|
60
|
+
/** Subscription billing period. */
|
|
61
|
+
subscriptionPeriod: SubscriptionPeriod;
|
|
62
|
+
/** Introductory offer, if any. */
|
|
63
|
+
introductoryPrice?: SubscriptionDiscount;
|
|
64
|
+
/** Promotional discounts (iOS). */
|
|
65
|
+
discounts?: SubscriptionDiscount[];
|
|
44
66
|
}
|
|
45
67
|
|
|
46
68
|
// ============================================================================
|
|
47
|
-
//
|
|
69
|
+
// Purchase Types
|
|
48
70
|
// ============================================================================
|
|
49
71
|
|
|
50
|
-
export
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
|
|
72
|
+
export type PurchaseState = 'purchased' | 'pending' | 'restored';
|
|
73
|
+
|
|
74
|
+
export interface IAPPurchase {
|
|
75
|
+
/** Product SKU. */
|
|
76
|
+
sku: string;
|
|
77
|
+
/** Store transaction ID. */
|
|
78
|
+
transactionId: string;
|
|
79
|
+
/** Transaction timestamp (ms since epoch). */
|
|
80
|
+
transactionDate: number;
|
|
81
|
+
/** Receipt data (base64 on iOS, JSON string on Android). */
|
|
82
|
+
transactionReceipt: string;
|
|
83
|
+
/** Google Play purchase token (Android only). */
|
|
84
|
+
purchaseToken?: string;
|
|
85
|
+
/** Whether the purchase has been acknowledged (Android only). */
|
|
86
|
+
isAcknowledged?: boolean;
|
|
87
|
+
/** Current purchase state. */
|
|
88
|
+
purchaseState: PurchaseState;
|
|
55
89
|
}
|
|
56
90
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
/** Amount in the smallest currency unit. */
|
|
61
|
-
amount: number;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export interface BillingAddressConfig {
|
|
65
|
-
/** Whether billing address is required. */
|
|
66
|
-
isRequired?: boolean;
|
|
67
|
-
/** Whether phone number is required. */
|
|
68
|
-
isPhoneNumberRequired?: boolean;
|
|
69
|
-
/** Address format: 'MIN' for name+postal, 'FULL' for complete address. */
|
|
70
|
-
format?: 'MIN' | 'FULL';
|
|
71
|
-
}
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// Configuration
|
|
93
|
+
// ============================================================================
|
|
72
94
|
|
|
73
|
-
export interface
|
|
95
|
+
export interface IAPConfig {
|
|
74
96
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
97
|
+
* If true, transactions are automatically finished after purchase.
|
|
98
|
+
* Default: false.
|
|
99
|
+
*
|
|
100
|
+
* WARNING: Set to false in production so you can validate receipts
|
|
101
|
+
* on your server before finishing transactions.
|
|
77
102
|
*/
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
/** Total payment amount. Used for display and for createPaymentMethod flow. */
|
|
81
|
-
amount: PaymentAmount;
|
|
82
|
-
|
|
83
|
-
/** Line items displayed on the payment sheet. */
|
|
84
|
-
lineItems?: PaymentLineItem[];
|
|
85
|
-
|
|
86
|
-
/** Billing address requirements. */
|
|
87
|
-
billingAddress?: BillingAddressConfig;
|
|
88
|
-
|
|
89
|
-
/** Preferred payment methods. If empty, all available methods are shown. */
|
|
90
|
-
allowedPaymentMethods?: PaymentMethodType[];
|
|
103
|
+
autoFinishTransactions?: boolean;
|
|
91
104
|
}
|
|
92
105
|
|
|
93
106
|
// ============================================================================
|
|
94
|
-
//
|
|
107
|
+
// Provider State
|
|
95
108
|
// ============================================================================
|
|
96
109
|
|
|
97
|
-
export
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
paymentMethodId?: string;
|
|
103
|
-
|
|
104
|
-
/** Stripe PaymentIntent ID (pi_xxx). Present for confirmPayment flow. */
|
|
105
|
-
paymentIntentId?: string;
|
|
110
|
+
export type IAPProviderState =
|
|
111
|
+
| 'uninitialized'
|
|
112
|
+
| 'initializing'
|
|
113
|
+
| 'ready'
|
|
114
|
+
| 'error';
|
|
106
115
|
|
|
107
|
-
|
|
108
|
-
|
|
116
|
+
export interface IAPProviderStatus {
|
|
117
|
+
/** Current state of the IAP connection. */
|
|
118
|
+
state: IAPProviderState;
|
|
119
|
+
/** Whether the native store is available and connected. */
|
|
120
|
+
isStoreAvailable: boolean;
|
|
121
|
+
/** Error if state is 'error'. */
|
|
122
|
+
error?: IAPError;
|
|
109
123
|
}
|
|
110
124
|
|
|
111
125
|
// ============================================================================
|
|
112
126
|
// Error Types
|
|
113
127
|
// ============================================================================
|
|
114
128
|
|
|
115
|
-
export type
|
|
129
|
+
export type IAPErrorCode =
|
|
116
130
|
| 'not_initialized'
|
|
117
131
|
| 'not_available'
|
|
118
132
|
| 'not_supported'
|
|
119
133
|
| 'user_cancelled'
|
|
120
|
-
| '
|
|
134
|
+
| 'already_owned'
|
|
135
|
+
| 'purchase_pending'
|
|
136
|
+
| 'item_unavailable'
|
|
121
137
|
| 'network_error'
|
|
122
|
-
| '
|
|
123
|
-
| 'invalid_request'
|
|
124
|
-
| 'provider_error'
|
|
138
|
+
| 'store_error'
|
|
125
139
|
| 'unknown';
|
|
126
140
|
|
|
127
|
-
export interface
|
|
128
|
-
code:
|
|
141
|
+
export interface IAPError {
|
|
142
|
+
code: IAPErrorCode;
|
|
129
143
|
message: string;
|
|
130
|
-
/** Original error from
|
|
144
|
+
/** Original error from react-native-iap. */
|
|
131
145
|
originalError?: unknown;
|
|
132
146
|
}
|
|
133
147
|
|
|
134
|
-
// ============================================================================
|
|
135
|
-
// Provider State
|
|
136
|
-
// ============================================================================
|
|
137
|
-
|
|
138
|
-
export type PaymentProviderState =
|
|
139
|
-
| 'uninitialized'
|
|
140
|
-
| 'initializing'
|
|
141
|
-
| 'ready'
|
|
142
|
-
| 'error';
|
|
143
|
-
|
|
144
|
-
export interface PaymentProviderStatus {
|
|
145
|
-
state: PaymentProviderState;
|
|
146
|
-
availablePaymentMethods: PaymentMethodAvailability[];
|
|
147
|
-
isPaymentAvailable: boolean;
|
|
148
|
-
error?: PaymentError;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
148
|
// ============================================================================
|
|
152
149
|
// Hook Types
|
|
153
150
|
// ============================================================================
|
|
154
151
|
|
|
155
|
-
export interface
|
|
156
|
-
/** If provided, auto-initializes the
|
|
157
|
-
config?:
|
|
158
|
-
/**
|
|
159
|
-
|
|
152
|
+
export interface UseIAPOptions {
|
|
153
|
+
/** If provided, auto-initializes the IAP connection. */
|
|
154
|
+
config?: IAPConfig;
|
|
155
|
+
/** Product SKUs to auto-fetch after initialization. */
|
|
156
|
+
productSkus?: string[];
|
|
157
|
+
/** Subscription SKUs to auto-fetch after initialization. */
|
|
158
|
+
subscriptionSkus?: string[];
|
|
160
159
|
}
|
|
161
160
|
|
|
162
|
-
export interface
|
|
161
|
+
export interface UseIAPResult {
|
|
163
162
|
// State
|
|
164
|
-
|
|
163
|
+
/** Whether the IAP connection is ready. */
|
|
165
164
|
isReady: boolean;
|
|
165
|
+
/** Whether a purchase or fetch is in progress. */
|
|
166
166
|
isProcessing: boolean;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
167
|
+
/** Current error, if any. */
|
|
168
|
+
error: IAPError | null;
|
|
169
|
+
/** Fetched products. */
|
|
170
|
+
products: IAPProduct[];
|
|
171
|
+
/** Fetched subscriptions. */
|
|
172
|
+
subscriptions: IAPSubscription[];
|
|
173
|
+
/** The most recent purchase (useful for handling post-purchase flows). */
|
|
174
|
+
currentPurchase: IAPPurchase | null;
|
|
172
175
|
|
|
173
176
|
// Actions
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
177
|
+
/** Initialize the IAP connection. */
|
|
178
|
+
initialize: (config?: IAPConfig) => Promise<void>;
|
|
179
|
+
/** Fetch products by SKU. */
|
|
180
|
+
getProducts: (skus: string[]) => Promise<IAPProduct[]>;
|
|
181
|
+
/** Fetch subscriptions by SKU. */
|
|
182
|
+
getSubscriptions: (skus: string[]) => Promise<IAPSubscription[]>;
|
|
183
|
+
/** Purchase a one-time product. */
|
|
184
|
+
purchaseProduct: (sku: string) => Promise<IAPPurchase>;
|
|
185
|
+
/** Purchase a subscription. */
|
|
186
|
+
purchaseSubscription: (sku: string, offerToken?: string) => Promise<IAPPurchase>;
|
|
187
|
+
/** Finish a transaction (call after server-side validation). */
|
|
188
|
+
finishTransaction: (purchase: IAPPurchase, isConsumable?: boolean) => Promise<void>;
|
|
189
|
+
/** Restore previous purchases. */
|
|
190
|
+
restorePurchases: () => Promise<IAPPurchase[]>;
|
|
191
|
+
/** Clear the current error. */
|
|
178
192
|
clearError: () => void;
|
|
179
193
|
}
|