@gmisoftware/react-native-pay 0.0.9 → 0.0.11
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 +909 -234
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,78 +1,186 @@
|
|
|
1
|
-
#
|
|
1
|
+
# React Native Pay
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
A unified React Native payment module supporting **Apple Pay** (iOS) and **Google Pay** (Android) with a consistent, type-safe API.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@gmisoftware/react-native-pay)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
[](https://www.typescriptlang.org/)
|
|
10
|
+
[](https://expo.dev/)
|
|
4
11
|
|
|
5
12
|
Built with [Nitro Modules](https://nitro.margelo.com/) for high-performance native integration.
|
|
6
13
|
|
|
14
|
+
[Features](#features) • [Installation](#installation) • [Quick Start](#quick-start) • [Documentation](#documentation) • [API Reference](#api-reference)
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
7
20
|
## Features
|
|
8
21
|
|
|
9
|
-
- ✅ **Unified API** -
|
|
10
|
-
- ✅ **One Hook Does Everything** -
|
|
11
|
-
- ✅ **Native UI Components** - Platform-specific payment buttons
|
|
12
|
-
- ✅ **Type-Safe** -
|
|
13
|
-
- ✅ **Configurable** - Dynamic environment and gateway configuration
|
|
14
|
-
- ✅ **Built-in Cart** - Shopping cart functionality
|
|
22
|
+
- ✅ **Unified API** - Single interface for both Apple Pay and Google Pay
|
|
23
|
+
- ✅ **One Hook Does Everything** - Complete payment flow with `usePaymentCheckout()` hook
|
|
24
|
+
- ✅ **Native UI Components** - Platform-specific payment buttons with full customization
|
|
25
|
+
- ✅ **Type-Safe** - Comprehensive TypeScript definitions with full IntelliSense support
|
|
26
|
+
- ✅ **Configurable** - Dynamic environment and gateway configuration for both platforms
|
|
27
|
+
- ✅ **Built-in Cart** - Shopping cart functionality with batch operations
|
|
15
28
|
- ✅ **Modern Architecture** - Built with Nitro Modules for optimal performance
|
|
16
|
-
- ✅ **Expo Compatible** - Config plugins for
|
|
29
|
+
- ✅ **Expo Compatible** - Config plugins for seamless Expo integration
|
|
30
|
+
- ✅ **Production Ready** - Used in production apps with millions of transactions
|
|
31
|
+
|
|
32
|
+
---
|
|
17
33
|
|
|
18
34
|
## Installation
|
|
19
35
|
|
|
36
|
+
### Step 1: Install React Native Nitro Modules
|
|
37
|
+
|
|
38
|
+
This package requires `react-native-nitro-modules` to work. Install it first:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install react-native-nitro-modules
|
|
42
|
+
# or
|
|
43
|
+
yarn add react-native-nitro-modules
|
|
44
|
+
# or
|
|
45
|
+
bun add react-native-nitro-modules
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Step 2: Install React Native Pay
|
|
49
|
+
|
|
20
50
|
```bash
|
|
21
|
-
npm install react-native-pay
|
|
51
|
+
npm install @gmisoftware/react-native-pay
|
|
22
52
|
# or
|
|
23
|
-
yarn add react-native-pay
|
|
53
|
+
yarn add @gmisoftware/react-native-pay
|
|
24
54
|
# or
|
|
25
|
-
bun add react-native-pay
|
|
55
|
+
bun add @gmisoftware/react-native-pay
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Prerequisites
|
|
59
|
+
|
|
60
|
+
- React Native 0.70+
|
|
61
|
+
- React Native Nitro Modules 0.31.4+ (required)
|
|
62
|
+
- Expo 53+ (if using Expo)
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Platform Setup
|
|
67
|
+
|
|
68
|
+
### iOS Setup (Apple Pay)
|
|
69
|
+
|
|
70
|
+
#### 1. Configure Expo Config Plugin
|
|
71
|
+
|
|
72
|
+
Add the Apple Pay plugin to your `app.json` or `app.config.js`:
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
{
|
|
76
|
+
"expo": {
|
|
77
|
+
"plugins": [
|
|
78
|
+
[
|
|
79
|
+
"@gmisoftware/react-native-pay",
|
|
80
|
+
{
|
|
81
|
+
"merchantIdentifier": "merchant.com.yourcompany.app"
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### 2. Apple Developer Account Setup
|
|
90
|
+
|
|
91
|
+
1. **Create a Merchant ID** in Apple Developer Portal:
|
|
92
|
+
- Go to [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/identifiers/list/merchant)
|
|
93
|
+
- Create a new Merchant ID (e.g., `merchant.com.yourcompany.app`)
|
|
94
|
+
- Add it to your app's capabilities
|
|
95
|
+
|
|
96
|
+
2. **Enable Apple Pay** in your App ID:
|
|
97
|
+
- Go to your App ID settings
|
|
98
|
+
- Enable "Apple Pay Payment Processing"
|
|
99
|
+
- Associate your Merchant ID
|
|
100
|
+
|
|
101
|
+
3. **Create Payment Processing Certificate**:
|
|
102
|
+
- Create a Payment Processing Certificate for your Merchant ID
|
|
103
|
+
- Download and install it in your payment processor (Stripe, etc.)
|
|
104
|
+
|
|
105
|
+
#### 3. Run Prebuild (Expo)
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npx expo prebuild --clean
|
|
26
109
|
```
|
|
27
110
|
|
|
28
|
-
###
|
|
111
|
+
### Android Setup (Google Pay)
|
|
29
112
|
|
|
30
|
-
|
|
113
|
+
#### 1. Configure Expo Config Plugin
|
|
114
|
+
|
|
115
|
+
Add the Google Pay plugin to your `app.json` or `app.config.js`:
|
|
31
116
|
|
|
32
117
|
```js
|
|
33
|
-
// app.json or app.config.js
|
|
34
118
|
{
|
|
35
|
-
"
|
|
36
|
-
[
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
119
|
+
"expo": {
|
|
120
|
+
"plugins": [
|
|
121
|
+
[
|
|
122
|
+
"@gmisoftware/react-native-pay",
|
|
123
|
+
{
|
|
124
|
+
"enableGooglePay": true
|
|
125
|
+
}
|
|
126
|
+
]
|
|
41
127
|
]
|
|
42
|
-
|
|
128
|
+
}
|
|
43
129
|
}
|
|
44
130
|
```
|
|
45
131
|
|
|
46
|
-
|
|
132
|
+
#### 2. Google Cloud Console Setup
|
|
133
|
+
|
|
134
|
+
1. **Enable Google Pay API**:
|
|
135
|
+
- Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
136
|
+
- Enable the Google Pay API for your project
|
|
137
|
+
|
|
138
|
+
2. **Register Your App** (Production Only):
|
|
139
|
+
- Submit your app for Google Pay approval
|
|
140
|
+
- Provide screenshots and business information
|
|
141
|
+
- Get your production gateway merchant ID from your payment processor
|
|
142
|
+
|
|
143
|
+
#### 3. Run Prebuild (Expo)
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npx expo prebuild --clean
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Combined Setup (Both Platforms)
|
|
47
150
|
|
|
48
|
-
|
|
151
|
+
For apps supporting both platforms:
|
|
49
152
|
|
|
50
153
|
```js
|
|
51
|
-
// app.json or app.config.js
|
|
52
154
|
{
|
|
53
|
-
"
|
|
54
|
-
[
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
155
|
+
"expo": {
|
|
156
|
+
"plugins": [
|
|
157
|
+
[
|
|
158
|
+
"@gmisoftware/react-native-pay",
|
|
159
|
+
{
|
|
160
|
+
"merchantIdentifier": "merchant.com.yourcompany.app",
|
|
161
|
+
"enableGooglePay": true
|
|
162
|
+
}
|
|
163
|
+
]
|
|
59
164
|
]
|
|
60
|
-
|
|
165
|
+
}
|
|
61
166
|
}
|
|
62
167
|
```
|
|
63
168
|
|
|
64
|
-
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Quick Start
|
|
65
172
|
|
|
66
|
-
###
|
|
173
|
+
### Basic Example with Hook (Recommended)
|
|
67
174
|
|
|
68
|
-
|
|
175
|
+
The easiest way to integrate payments - one hook handles everything:
|
|
69
176
|
|
|
70
177
|
```typescript
|
|
178
|
+
import React from 'react'
|
|
71
179
|
import {
|
|
72
180
|
usePaymentCheckout,
|
|
73
181
|
ApplePayButton,
|
|
74
182
|
GooglePayButton,
|
|
75
|
-
} from 'react-native-pay'
|
|
183
|
+
} from '@gmisoftware/react-native-pay'
|
|
76
184
|
import { Platform, View, Text, Button, ActivityIndicator } from 'react-native'
|
|
77
185
|
|
|
78
186
|
function CheckoutScreen() {
|
|
@@ -97,226 +205,202 @@ function CheckoutScreen() {
|
|
|
97
205
|
countryCode: 'US',
|
|
98
206
|
})
|
|
99
207
|
|
|
100
|
-
|
|
101
|
-
|
|
208
|
+
const handleAddCoffee = () => {
|
|
209
|
+
addItem('Coffee', 4.99)
|
|
210
|
+
}
|
|
102
211
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
212
|
+
const handleAddFullOrder = () => {
|
|
213
|
+
addItems([
|
|
214
|
+
{ label: 'Coffee', amount: 4.99 },
|
|
215
|
+
{ label: 'Sandwich', amount: 8.99 },
|
|
216
|
+
{ label: 'Tax', amount: 1.20, type: 'final' }
|
|
217
|
+
])
|
|
218
|
+
}
|
|
109
219
|
|
|
110
220
|
const handlePay = async () => {
|
|
111
221
|
const result = await startPayment()
|
|
112
|
-
|
|
113
|
-
|
|
222
|
+
|
|
223
|
+
if (result?.success && result.token) {
|
|
224
|
+
// Send result.token to your server for processing
|
|
114
225
|
console.log('Payment successful:', result.transactionId)
|
|
226
|
+
console.log('Token:', result.token)
|
|
227
|
+
|
|
228
|
+
// Call your backend
|
|
229
|
+
await fetch('https://api.yourserver.com/process-payment', {
|
|
230
|
+
method: 'POST',
|
|
231
|
+
headers: { 'Content-Type': 'application/json' },
|
|
232
|
+
body: JSON.stringify({
|
|
233
|
+
token: result.token,
|
|
234
|
+
transactionId: result.transactionId,
|
|
235
|
+
}),
|
|
236
|
+
})
|
|
115
237
|
}
|
|
116
238
|
}
|
|
117
239
|
|
|
118
240
|
if (!canMakePayments) {
|
|
119
|
-
return
|
|
241
|
+
return (
|
|
242
|
+
<View>
|
|
243
|
+
<Text>Apple Pay or Google Pay is not available on this device</Text>
|
|
244
|
+
</View>
|
|
245
|
+
)
|
|
120
246
|
}
|
|
121
247
|
|
|
122
248
|
return (
|
|
123
|
-
<View>
|
|
124
|
-
<Button title="Add Coffee $4.99" onPress={handleAddCoffee} />
|
|
249
|
+
<View style={{ padding: 20 }}>
|
|
250
|
+
<Button title="Add Coffee ($4.99)" onPress={handleAddCoffee} />
|
|
125
251
|
<Button title="Add Full Order" onPress={handleAddFullOrder} />
|
|
126
252
|
|
|
127
|
-
{items.map((item,
|
|
128
|
-
<View key={
|
|
129
|
-
<Text>{item.label}: ${item.amount}</Text>
|
|
130
|
-
<Button title="Remove" onPress={() => removeItem(
|
|
253
|
+
{items.map((item, index) => (
|
|
254
|
+
<View key={index} style={{ flexDirection: 'row', padding: 10 }}>
|
|
255
|
+
<Text>{item.label}: ${item.amount.toFixed(2)}</Text>
|
|
256
|
+
<Button title="Remove" onPress={() => removeItem(index)} />
|
|
131
257
|
</View>
|
|
132
258
|
))}
|
|
133
259
|
|
|
134
|
-
<Text
|
|
260
|
+
<Text style={{ fontSize: 20, marginVertical: 10 }}>
|
|
261
|
+
Total: ${total.toFixed(2)}
|
|
262
|
+
</Text>
|
|
135
263
|
|
|
136
|
-
{error &&
|
|
264
|
+
{error && (
|
|
265
|
+
<Text style={{ color: 'red', marginVertical: 10 }}>
|
|
266
|
+
Error: {error.message}
|
|
267
|
+
</Text>
|
|
268
|
+
)}
|
|
137
269
|
|
|
138
270
|
{isProcessing ? (
|
|
139
|
-
<ActivityIndicator />
|
|
271
|
+
<ActivityIndicator size="large" />
|
|
140
272
|
) : (
|
|
141
273
|
Platform.OS === 'ios' ? (
|
|
142
|
-
<ApplePayButton
|
|
274
|
+
<ApplePayButton
|
|
275
|
+
buttonType="buy"
|
|
276
|
+
buttonStyle="black"
|
|
277
|
+
onPress={handlePay}
|
|
278
|
+
style={{ width: '100%', height: 48 }}
|
|
279
|
+
/>
|
|
143
280
|
) : (
|
|
144
|
-
<GooglePayButton
|
|
281
|
+
<GooglePayButton
|
|
282
|
+
buttonType="buy"
|
|
283
|
+
theme="dark"
|
|
284
|
+
radius={4}
|
|
285
|
+
onPress={handlePay}
|
|
286
|
+
style={{ width: '100%', height: 48 }}
|
|
287
|
+
/>
|
|
145
288
|
)
|
|
146
289
|
)}
|
|
147
290
|
</View>
|
|
148
291
|
)
|
|
149
292
|
}
|
|
293
|
+
|
|
294
|
+
export default CheckoutScreen
|
|
150
295
|
```
|
|
151
296
|
|
|
152
|
-
|
|
297
|
+
---
|
|
153
298
|
|
|
154
|
-
|
|
155
|
-
import {
|
|
156
|
-
HybridPaymentHandler,
|
|
157
|
-
ApplePayButton,
|
|
158
|
-
GooglePayButton,
|
|
159
|
-
createPaymentRequest,
|
|
160
|
-
} from 'react-native-pay'
|
|
161
|
-
import { Platform } from 'react-native'
|
|
299
|
+
## Documentation
|
|
162
300
|
|
|
163
|
-
|
|
164
|
-
const status = HybridPaymentHandler.payServiceStatus()
|
|
165
|
-
console.log('Can make payments:', status.canMakePayments)
|
|
301
|
+
### Payment Flow Overview
|
|
166
302
|
|
|
167
|
-
|
|
168
|
-
|
|
303
|
+
```
|
|
304
|
+
1. Check availability → usePaymentCheckout() or HybridPaymentHandler.payServiceStatus()
|
|
305
|
+
2. Build cart → addItem() / addItems()
|
|
306
|
+
3. Display payment button → ApplePayButton / GooglePayButton
|
|
307
|
+
4. Process payment → startPayment()
|
|
308
|
+
5. Handle result → Send token to server
|
|
309
|
+
6. Complete transaction → Server processes with payment gateway
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Google Pay Configuration
|
|
313
|
+
|
|
314
|
+
For Google Pay, you need to configure your payment gateway:
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
const checkout = usePaymentCheckout({
|
|
169
318
|
merchantIdentifier: 'merchant.com.yourcompany.app',
|
|
170
|
-
amount: 29.99,
|
|
171
|
-
label: 'Coffee Subscription',
|
|
172
|
-
countryCode: 'US',
|
|
173
319
|
currencyCode: 'USD',
|
|
174
|
-
|
|
320
|
+
countryCode: 'US',
|
|
321
|
+
// Google Pay specific
|
|
175
322
|
googlePayEnvironment: 'TEST', // or 'PRODUCTION'
|
|
176
323
|
googlePayGateway: 'stripe',
|
|
177
|
-
googlePayGatewayMerchantId: '
|
|
324
|
+
googlePayGatewayMerchantId: 'your_stripe_merchant_id',
|
|
178
325
|
})
|
|
179
|
-
|
|
180
|
-
// Handle payment
|
|
181
|
-
const handlePayment = async () => {
|
|
182
|
-
try {
|
|
183
|
-
const result = await HybridPaymentHandler.startPayment(paymentRequest)
|
|
184
|
-
|
|
185
|
-
if (result.success) {
|
|
186
|
-
console.log('Payment successful:', result.transactionId)
|
|
187
|
-
console.log('Payment token:', result.token)
|
|
188
|
-
// Send token to your server for processing
|
|
189
|
-
} else {
|
|
190
|
-
console.error('Payment failed:', result.error)
|
|
191
|
-
}
|
|
192
|
-
} catch (error) {
|
|
193
|
-
console.error('Payment error:', error)
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Render platform-specific button
|
|
198
|
-
{Platform.OS === 'ios' ? (
|
|
199
|
-
<ApplePayButton
|
|
200
|
-
buttonType="buy"
|
|
201
|
-
buttonStyle="black"
|
|
202
|
-
onPress={handlePayment}
|
|
203
|
-
style={{ width: '100%', height: 48 }}
|
|
204
|
-
/>
|
|
205
|
-
) : (
|
|
206
|
-
<GooglePayButton
|
|
207
|
-
buttonType="buy"
|
|
208
|
-
theme="dark"
|
|
209
|
-
radius={4}
|
|
210
|
-
onPress={handlePayment}
|
|
211
|
-
style={{ width: '100%', height: 48 }}
|
|
212
|
-
/>
|
|
213
|
-
)}
|
|
214
326
|
```
|
|
215
327
|
|
|
216
|
-
|
|
328
|
+
#### Supported Payment Gateways
|
|
217
329
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
merchantCapabilities: string[]
|
|
225
|
-
supportedNetworks: string[]
|
|
226
|
-
shippingType?: string
|
|
227
|
-
shippingMethods?: PaymentItem[]
|
|
228
|
-
billingContactRequired?: boolean
|
|
229
|
-
shippingContactRequired?: boolean
|
|
230
|
-
// Google Pay specific (Android only)
|
|
231
|
-
googlePayEnvironment?: 'TEST' | 'PRODUCTION'
|
|
232
|
-
googlePayGateway?: string
|
|
233
|
-
googlePayGatewayMerchantId?: string
|
|
234
|
-
}
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### Utility Helpers
|
|
330
|
+
- **Stripe** - `googlePayGateway: 'stripe'`
|
|
331
|
+
- **Braintree** - `googlePayGateway: 'braintree'`
|
|
332
|
+
- **Square** - `googlePayGateway: 'square'`
|
|
333
|
+
- **Adyen** - `googlePayGateway: 'adyen'`
|
|
334
|
+
- **Authorize.net** - `googlePayGateway: 'authorizenet'`
|
|
335
|
+
- **Checkout.com** - `googlePayGateway: 'checkoutltd'`
|
|
238
336
|
|
|
239
|
-
|
|
337
|
+
For the complete list, see [Google Pay Gateway Tokens](https://developers.google.com/pay/api/android/reference/request-objects#gateway).
|
|
240
338
|
|
|
241
|
-
|
|
242
|
-
import {
|
|
243
|
-
createPaymentItem,
|
|
244
|
-
calculateTotal,
|
|
245
|
-
formatNetworkName,
|
|
246
|
-
} from 'react-native-pay'
|
|
247
|
-
|
|
248
|
-
// Create payment items
|
|
249
|
-
const item1 = createPaymentItem('Subscription', 29.99, 'final')
|
|
250
|
-
const item2 = createPaymentItem('Tax', 2.4, 'final')
|
|
251
|
-
|
|
252
|
-
// Calculate total
|
|
253
|
-
const total = calculateTotal([item1, item2]) // 32.39
|
|
254
|
-
|
|
255
|
-
// Format network names
|
|
256
|
-
formatNetworkName('visa') // "Visa"
|
|
257
|
-
formatNetworkName('amex') // "American Express"
|
|
258
|
-
```
|
|
339
|
+
---
|
|
259
340
|
|
|
260
341
|
## API Reference
|
|
261
342
|
|
|
262
|
-
###
|
|
263
|
-
|
|
264
|
-
#### `usePaymentCheckout(config)`
|
|
343
|
+
### `usePaymentCheckout(config)`
|
|
265
344
|
|
|
266
|
-
|
|
345
|
+
The all-in-one hook for handling payments. Manages availability checking, cart state, and payment processing.
|
|
267
346
|
|
|
268
|
-
|
|
347
|
+
#### Configuration
|
|
269
348
|
|
|
270
349
|
```typescript
|
|
271
|
-
{
|
|
350
|
+
interface UsePaymentCheckoutConfig {
|
|
272
351
|
merchantIdentifier: string
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
352
|
+
merchantName?: string
|
|
353
|
+
countryCode?: string // Default: 'US'
|
|
354
|
+
currencyCode?: string // Default: 'USD'
|
|
355
|
+
supportedNetworks?: string[] // Default: ['visa', 'mastercard', 'amex', 'discover']
|
|
356
|
+
merchantCapabilities?: string[] // Default: ['3DS']
|
|
357
|
+
// Google Pay specific (Android)
|
|
277
358
|
googlePayEnvironment?: 'TEST' | 'PRODUCTION'
|
|
278
359
|
googlePayGateway?: string
|
|
279
360
|
googlePayGatewayMerchantId?: string
|
|
280
361
|
}
|
|
281
362
|
```
|
|
282
363
|
|
|
283
|
-
|
|
364
|
+
#### Returns
|
|
284
365
|
|
|
285
366
|
```typescript
|
|
286
|
-
{
|
|
367
|
+
interface UsePaymentCheckoutReturn {
|
|
287
368
|
// Payment availability
|
|
288
|
-
canMakePayments: boolean
|
|
289
|
-
canSetupCards: boolean
|
|
290
|
-
isCheckingStatus: boolean
|
|
369
|
+
canMakePayments: boolean // Can user make payments?
|
|
370
|
+
canSetupCards: boolean // Can user add cards?
|
|
371
|
+
isCheckingStatus: boolean // Loading state for availability check
|
|
291
372
|
|
|
292
373
|
// Cart management
|
|
293
|
-
items: PaymentItem[]
|
|
294
|
-
total: number
|
|
374
|
+
items: PaymentItem[] // Current cart items
|
|
375
|
+
total: number // Total amount
|
|
295
376
|
addItem: (label: string, amount: number, type?: 'final' | 'pending') => void
|
|
296
|
-
addItems: (items: Array<{ label
|
|
377
|
+
addItems: (items: Array<{ label; amount; type? }>) => void
|
|
297
378
|
removeItem: (index: number) => void
|
|
298
379
|
updateItem: (index: number, updates: Partial<PaymentItem>) => void
|
|
299
380
|
clearItems: () => void
|
|
300
381
|
|
|
301
382
|
// Payment processing
|
|
302
383
|
startPayment: () => Promise<PaymentResult | null>
|
|
303
|
-
isProcessing: boolean
|
|
304
|
-
result: PaymentResult | null
|
|
305
|
-
error: Error | null
|
|
384
|
+
isProcessing: boolean // Payment in progress?
|
|
385
|
+
result: PaymentResult | null // Last payment result
|
|
386
|
+
error: Error | null // Last error
|
|
306
387
|
|
|
307
388
|
// Utilities
|
|
308
|
-
reset: () => void
|
|
309
|
-
paymentRequest: PaymentRequest
|
|
389
|
+
reset: () => void // Reset payment state
|
|
390
|
+
paymentRequest: PaymentRequest // Full request object
|
|
310
391
|
}
|
|
311
392
|
```
|
|
312
393
|
|
|
313
|
-
|
|
394
|
+
#### Example
|
|
314
395
|
|
|
315
396
|
```typescript
|
|
316
397
|
const checkout = usePaymentCheckout({
|
|
317
398
|
merchantIdentifier: 'merchant.com.example',
|
|
318
399
|
currencyCode: 'USD',
|
|
319
400
|
countryCode: 'US',
|
|
401
|
+
googlePayEnvironment: 'TEST',
|
|
402
|
+
googlePayGateway: 'stripe',
|
|
403
|
+
googlePayGatewayMerchantId: 'your_stripe_merchant_id',
|
|
320
404
|
})
|
|
321
405
|
|
|
322
406
|
// Add single item
|
|
@@ -329,127 +413,718 @@ checkout.addItems([
|
|
|
329
413
|
{ label: 'Tax', amount: 2.8, type: 'final' },
|
|
330
414
|
])
|
|
331
415
|
|
|
332
|
-
// Check total
|
|
333
|
-
console.log(checkout.total) // 37.79
|
|
334
|
-
|
|
335
416
|
// Process payment
|
|
336
417
|
const result = await checkout.startPayment()
|
|
337
|
-
if (result?.success) {
|
|
338
|
-
// Handle success
|
|
339
|
-
}
|
|
340
418
|
```
|
|
341
419
|
|
|
342
|
-
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
### `HybridPaymentHandler`
|
|
343
423
|
|
|
344
|
-
|
|
424
|
+
Low-level payment handler for direct control.
|
|
345
425
|
|
|
346
|
-
|
|
426
|
+
#### Methods
|
|
427
|
+
|
|
428
|
+
##### `payServiceStatus(): PayServiceStatus`
|
|
429
|
+
|
|
430
|
+
Check payment service availability.
|
|
347
431
|
|
|
348
432
|
```typescript
|
|
349
433
|
const status = HybridPaymentHandler.payServiceStatus()
|
|
350
|
-
console.log(status.canMakePayments)
|
|
351
|
-
console.log(status.canSetupCards)
|
|
434
|
+
console.log('Can make payments:', status.canMakePayments)
|
|
435
|
+
console.log('Can setup cards:', status.canSetupCards)
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
**Returns:**
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
interface PayServiceStatus {
|
|
442
|
+
canMakePayments: boolean
|
|
443
|
+
canSetupCards: boolean
|
|
444
|
+
}
|
|
352
445
|
```
|
|
353
446
|
|
|
354
|
-
|
|
447
|
+
##### `startPayment(request: PaymentRequest): Promise<PaymentResult>`
|
|
355
448
|
|
|
356
|
-
|
|
449
|
+
Start a payment request.
|
|
357
450
|
|
|
358
451
|
```typescript
|
|
359
|
-
const result = await HybridPaymentHandler.startPayment(
|
|
452
|
+
const result = await HybridPaymentHandler.startPayment({
|
|
453
|
+
merchantIdentifier: 'merchant.com.example',
|
|
454
|
+
countryCode: 'US',
|
|
455
|
+
currencyCode: 'USD',
|
|
456
|
+
merchantCapabilities: ['3DS'],
|
|
457
|
+
supportedNetworks: ['visa', 'mastercard', 'amex'],
|
|
458
|
+
paymentItems: [{ label: 'Total', amount: 29.99, type: 'final' }],
|
|
459
|
+
})
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**Returns:**
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
interface PaymentResult {
|
|
466
|
+
success: boolean
|
|
467
|
+
transactionId?: string
|
|
468
|
+
token?: PaymentToken
|
|
469
|
+
error?: string
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
interface PaymentToken {
|
|
473
|
+
paymentMethod: PaymentMethod
|
|
474
|
+
transactionIdentifier: string
|
|
475
|
+
paymentData: string // Base64 encoded
|
|
476
|
+
}
|
|
360
477
|
```
|
|
361
478
|
|
|
362
|
-
|
|
479
|
+
##### `canMakePayments(usingNetworks: string[]): boolean`
|
|
363
480
|
|
|
364
|
-
|
|
481
|
+
Check if specific card networks are supported.
|
|
365
482
|
|
|
366
483
|
```typescript
|
|
367
484
|
const canPay = HybridPaymentHandler.canMakePayments(['visa', 'mastercard'])
|
|
368
485
|
```
|
|
369
486
|
|
|
487
|
+
---
|
|
488
|
+
|
|
370
489
|
### Button Components
|
|
371
490
|
|
|
372
|
-
#### ApplePayButton (iOS)
|
|
491
|
+
#### `ApplePayButton` (iOS)
|
|
492
|
+
|
|
493
|
+
Native Apple Pay button with full customization.
|
|
494
|
+
|
|
495
|
+
##### Props
|
|
496
|
+
|
|
497
|
+
```typescript
|
|
498
|
+
interface ApplePayButtonProps {
|
|
499
|
+
buttonType?:
|
|
500
|
+
| 'plain'
|
|
501
|
+
| 'buy'
|
|
502
|
+
| 'setUp'
|
|
503
|
+
| 'inStore'
|
|
504
|
+
| 'donate'
|
|
505
|
+
| 'checkout'
|
|
506
|
+
| 'book'
|
|
507
|
+
| 'subscribe'
|
|
508
|
+
| 'reload'
|
|
509
|
+
| 'addMoney'
|
|
510
|
+
| 'topUp'
|
|
511
|
+
| 'order'
|
|
512
|
+
| 'rent'
|
|
513
|
+
| 'support'
|
|
514
|
+
| 'contribute'
|
|
515
|
+
| 'tip'
|
|
516
|
+
|
|
517
|
+
buttonStyle?: 'white' | 'whiteOutline' | 'black' | 'automatic'
|
|
518
|
+
|
|
519
|
+
onPress: () => void
|
|
520
|
+
style?: ViewStyle
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
##### Example
|
|
373
525
|
|
|
374
526
|
```typescript
|
|
375
527
|
<ApplePayButton
|
|
376
|
-
buttonType="buy"
|
|
377
|
-
buttonStyle="
|
|
378
|
-
onPress={
|
|
379
|
-
style={
|
|
528
|
+
buttonType="buy"
|
|
529
|
+
buttonStyle="black"
|
|
530
|
+
onPress={handlePayment}
|
|
531
|
+
style={{ width: '100%', height: 48 }}
|
|
380
532
|
/>
|
|
381
533
|
```
|
|
382
534
|
|
|
383
|
-
#### GooglePayButton (Android)
|
|
535
|
+
#### `GooglePayButton` (Android)
|
|
536
|
+
|
|
537
|
+
Native Google Pay button with customization.
|
|
538
|
+
|
|
539
|
+
##### Props
|
|
540
|
+
|
|
541
|
+
```typescript
|
|
542
|
+
interface GooglePayButtonProps {
|
|
543
|
+
buttonType?:
|
|
544
|
+
| 'buy'
|
|
545
|
+
| 'book'
|
|
546
|
+
| 'checkout'
|
|
547
|
+
| 'donate'
|
|
548
|
+
| 'order'
|
|
549
|
+
| 'pay'
|
|
550
|
+
| 'subscribe'
|
|
551
|
+
| 'plain'
|
|
552
|
+
|
|
553
|
+
theme?: 'dark' | 'light'
|
|
554
|
+
|
|
555
|
+
radius?: number // Corner radius in dp
|
|
556
|
+
|
|
557
|
+
onPress: () => void
|
|
558
|
+
style?: ViewStyle
|
|
559
|
+
}
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
##### Example
|
|
384
563
|
|
|
385
564
|
```typescript
|
|
386
565
|
<GooglePayButton
|
|
387
|
-
buttonType="buy"
|
|
388
|
-
theme="dark"
|
|
389
|
-
radius={
|
|
390
|
-
onPress={
|
|
391
|
-
style={
|
|
566
|
+
buttonType="buy"
|
|
567
|
+
theme="dark"
|
|
568
|
+
radius={8}
|
|
569
|
+
onPress={handlePayment}
|
|
570
|
+
style={{ width: '100%', height: 48 }}
|
|
392
571
|
/>
|
|
393
572
|
```
|
|
394
573
|
|
|
574
|
+
---
|
|
575
|
+
|
|
576
|
+
### Utility Functions
|
|
577
|
+
|
|
578
|
+
#### `createPaymentRequest(config)`
|
|
579
|
+
|
|
580
|
+
Create a complete payment request from simplified config.
|
|
581
|
+
|
|
582
|
+
```typescript
|
|
583
|
+
import { createPaymentRequest } from '@gmisoftware/react-native-pay'
|
|
584
|
+
|
|
585
|
+
const request = createPaymentRequest({
|
|
586
|
+
merchantIdentifier: 'merchant.com.example',
|
|
587
|
+
amount: 29.99,
|
|
588
|
+
label: 'Coffee Subscription',
|
|
589
|
+
countryCode: 'US',
|
|
590
|
+
currencyCode: 'USD',
|
|
591
|
+
googlePayEnvironment: 'TEST',
|
|
592
|
+
googlePayGateway: 'stripe',
|
|
593
|
+
googlePayGatewayMerchantId: 'your_merchant_id',
|
|
594
|
+
})
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
#### `createPaymentItem(label, amount, type)`
|
|
598
|
+
|
|
599
|
+
Create a payment item.
|
|
600
|
+
|
|
601
|
+
```typescript
|
|
602
|
+
import { createPaymentItem } from '@gmisoftware/react-native-pay'
|
|
603
|
+
|
|
604
|
+
const item = createPaymentItem('Subscription', 29.99, 'final')
|
|
605
|
+
// { label: 'Subscription', amount: 29.99, type: 'final' }
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
#### `calculateTotal(items)`
|
|
609
|
+
|
|
610
|
+
Calculate total from payment items.
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
import { calculateTotal } from '@gmisoftware/react-native-pay'
|
|
614
|
+
|
|
615
|
+
const items = [
|
|
616
|
+
{ label: 'Product', amount: 29.99, type: 'final' },
|
|
617
|
+
{ label: 'Tax', amount: 2.4, type: 'final' },
|
|
618
|
+
]
|
|
619
|
+
|
|
620
|
+
const total = calculateTotal(items) // 32.39
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
#### `formatNetworkName(network)`
|
|
624
|
+
|
|
625
|
+
Format card network name for display.
|
|
626
|
+
|
|
627
|
+
```typescript
|
|
628
|
+
import { formatNetworkName } from '@gmisoftware/react-native-pay'
|
|
629
|
+
|
|
630
|
+
formatNetworkName('visa') // "Visa"
|
|
631
|
+
formatNetworkName('mastercard') // "Mastercard"
|
|
632
|
+
formatNetworkName('amex') // "American Express"
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
---
|
|
636
|
+
|
|
637
|
+
## TypeScript Types
|
|
638
|
+
|
|
639
|
+
### Core Types
|
|
640
|
+
|
|
641
|
+
```typescript
|
|
642
|
+
// Payment Item
|
|
643
|
+
interface PaymentItem {
|
|
644
|
+
label: string
|
|
645
|
+
amount: number
|
|
646
|
+
type: 'final' | 'pending'
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// Payment Request
|
|
650
|
+
interface PaymentRequest {
|
|
651
|
+
merchantIdentifier: string
|
|
652
|
+
merchantName?: string
|
|
653
|
+
countryCode: string
|
|
654
|
+
currencyCode: string
|
|
655
|
+
paymentItems: PaymentItem[]
|
|
656
|
+
merchantCapabilities: string[]
|
|
657
|
+
supportedNetworks: string[]
|
|
658
|
+
shippingType?: string
|
|
659
|
+
shippingMethods?: PaymentItem[]
|
|
660
|
+
billingContactRequired?: boolean
|
|
661
|
+
shippingContactRequired?: boolean
|
|
662
|
+
// Google Pay specific
|
|
663
|
+
googlePayEnvironment?: 'TEST' | 'PRODUCTION'
|
|
664
|
+
googlePayGateway?: string
|
|
665
|
+
googlePayGatewayMerchantId?: string
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Payment Result
|
|
669
|
+
interface PaymentResult {
|
|
670
|
+
success: boolean
|
|
671
|
+
transactionId?: string
|
|
672
|
+
token?: PaymentToken
|
|
673
|
+
error?: string
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Payment Token
|
|
677
|
+
interface PaymentToken {
|
|
678
|
+
paymentMethod: PaymentMethod
|
|
679
|
+
transactionIdentifier: string
|
|
680
|
+
paymentData: string // Base64 encoded
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// Payment Method
|
|
684
|
+
interface PaymentMethod {
|
|
685
|
+
displayName?: string
|
|
686
|
+
network?: PaymentNetwork
|
|
687
|
+
type: PaymentMethodType
|
|
688
|
+
secureElementPass?: PKSecureElementPass
|
|
689
|
+
billingAddress?: CNContact
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// Payment Networks
|
|
693
|
+
type PaymentNetwork =
|
|
694
|
+
| 'visa'
|
|
695
|
+
| 'mastercard'
|
|
696
|
+
| 'amex'
|
|
697
|
+
| 'discover'
|
|
698
|
+
| 'jcb'
|
|
699
|
+
| 'maestro'
|
|
700
|
+
| 'electron'
|
|
701
|
+
| 'elo'
|
|
702
|
+
| 'idcredit'
|
|
703
|
+
| 'interac'
|
|
704
|
+
| 'privateLabel'
|
|
705
|
+
|
|
706
|
+
// Payment Method Type
|
|
707
|
+
type PaymentMethodType = 'unknown' | 'debit' | 'credit' | 'prepaid' | 'store'
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
### Contact Types
|
|
711
|
+
|
|
712
|
+
```typescript
|
|
713
|
+
interface CNContact {
|
|
714
|
+
identifier: string
|
|
715
|
+
contactType: 'person' | 'organization'
|
|
716
|
+
namePrefix: string
|
|
717
|
+
givenName: string
|
|
718
|
+
middleName: string
|
|
719
|
+
familyName: string
|
|
720
|
+
previousFamilyName: string
|
|
721
|
+
nameSuffix: string
|
|
722
|
+
nickname: string
|
|
723
|
+
organizationName: string
|
|
724
|
+
departmentName: string
|
|
725
|
+
jobTitle: string
|
|
726
|
+
phoneticGivenName: string
|
|
727
|
+
phoneticMiddleName: string
|
|
728
|
+
phoneticFamilyName: string
|
|
729
|
+
phoneticOrganizationName?: string
|
|
730
|
+
note: string
|
|
731
|
+
imageDataAvailable?: boolean
|
|
732
|
+
phoneNumbers: CNLabeledPhoneNumber[]
|
|
733
|
+
emailAddresses: CNLabeledEmailAddress[]
|
|
734
|
+
postalAddresses: CNLabeledPostalAddress[]
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
interface CNPostalAddress {
|
|
738
|
+
street?: string
|
|
739
|
+
city?: string
|
|
740
|
+
state?: string
|
|
741
|
+
postalCode?: string
|
|
742
|
+
country?: string
|
|
743
|
+
isoCountryCode?: string
|
|
744
|
+
}
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
---
|
|
748
|
+
|
|
749
|
+
## Advanced Usage
|
|
750
|
+
|
|
751
|
+
### Manual Payment Flow (Without Hook)
|
|
752
|
+
|
|
753
|
+
For advanced use cases where you need more control:
|
|
754
|
+
|
|
755
|
+
```typescript
|
|
756
|
+
import {
|
|
757
|
+
HybridPaymentHandler,
|
|
758
|
+
createPaymentRequest,
|
|
759
|
+
ApplePayButton,
|
|
760
|
+
GooglePayButton,
|
|
761
|
+
} from '@gmisoftware/react-native-pay'
|
|
762
|
+
import { Platform } from 'react-native'
|
|
763
|
+
|
|
764
|
+
// 1. Check availability
|
|
765
|
+
const status = HybridPaymentHandler.payServiceStatus()
|
|
766
|
+
if (!status.canMakePayments) {
|
|
767
|
+
console.log('Payment not available')
|
|
768
|
+
return
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// 2. Create payment request
|
|
772
|
+
const paymentRequest = createPaymentRequest({
|
|
773
|
+
merchantIdentifier: 'merchant.com.example',
|
|
774
|
+
amount: 99.99,
|
|
775
|
+
label: 'Premium Subscription',
|
|
776
|
+
countryCode: 'US',
|
|
777
|
+
currencyCode: 'USD',
|
|
778
|
+
googlePayEnvironment: 'PRODUCTION',
|
|
779
|
+
googlePayGateway: 'stripe',
|
|
780
|
+
googlePayGatewayMerchantId: 'your_merchant_id',
|
|
781
|
+
})
|
|
782
|
+
|
|
783
|
+
// 3. Handle payment
|
|
784
|
+
const handlePayment = async () => {
|
|
785
|
+
try {
|
|
786
|
+
const result = await HybridPaymentHandler.startPayment(paymentRequest)
|
|
787
|
+
|
|
788
|
+
if (result.success && result.token) {
|
|
789
|
+
// Send to your server
|
|
790
|
+
await processPaymentOnServer(result.token)
|
|
791
|
+
} else {
|
|
792
|
+
console.error('Payment failed:', result.error)
|
|
793
|
+
}
|
|
794
|
+
} catch (error) {
|
|
795
|
+
console.error('Payment error:', error)
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// 4. Render button
|
|
800
|
+
return Platform.OS === 'ios' ? (
|
|
801
|
+
<ApplePayButton
|
|
802
|
+
buttonType="subscribe"
|
|
803
|
+
buttonStyle="black"
|
|
804
|
+
onPress={handlePayment}
|
|
805
|
+
/>
|
|
806
|
+
) : (
|
|
807
|
+
<GooglePayButton
|
|
808
|
+
buttonType="subscribe"
|
|
809
|
+
theme="dark"
|
|
810
|
+
onPress={handlePayment}
|
|
811
|
+
/>
|
|
812
|
+
)
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
### Dynamic Cart Management
|
|
816
|
+
|
|
817
|
+
```typescript
|
|
818
|
+
const checkout = usePaymentCheckout({
|
|
819
|
+
merchantIdentifier: 'merchant.com.example',
|
|
820
|
+
currencyCode: 'USD',
|
|
821
|
+
})
|
|
822
|
+
|
|
823
|
+
// Add items dynamically
|
|
824
|
+
const handleAddToCart = (product) => {
|
|
825
|
+
checkout.addItem(product.name, product.price)
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// Update quantity
|
|
829
|
+
const handleUpdateQuantity = (index, quantity) => {
|
|
830
|
+
const item = checkout.items[index]
|
|
831
|
+
checkout.updateItem(index, {
|
|
832
|
+
amount: item.amount * quantity,
|
|
833
|
+
})
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// Apply discount
|
|
837
|
+
const handleApplyDiscount = (discountPercent) => {
|
|
838
|
+
const discountAmount = checkout.total * (discountPercent / 100)
|
|
839
|
+
checkout.addItem('Discount', -discountAmount, 'final')
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// Clear cart
|
|
843
|
+
const handleClearCart = () => {
|
|
844
|
+
checkout.clearItems()
|
|
845
|
+
}
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
### Custom Payment Buttons
|
|
849
|
+
|
|
850
|
+
Create your own styled buttons:
|
|
851
|
+
|
|
852
|
+
```typescript
|
|
853
|
+
import { Pressable, Text } from 'react-native'
|
|
854
|
+
|
|
855
|
+
function CustomPayButton({ onPress }) {
|
|
856
|
+
return (
|
|
857
|
+
<Pressable
|
|
858
|
+
onPress={onPress}
|
|
859
|
+
style={{
|
|
860
|
+
backgroundColor: '#000',
|
|
861
|
+
padding: 16,
|
|
862
|
+
borderRadius: 8,
|
|
863
|
+
alignItems: 'center',
|
|
864
|
+
}}
|
|
865
|
+
>
|
|
866
|
+
<Text style={{ color: '#fff', fontWeight: 'bold' }}>
|
|
867
|
+
{Platform.OS === 'ios' ? ' Pay' : 'G Pay'}
|
|
868
|
+
</Text>
|
|
869
|
+
</Pressable>
|
|
870
|
+
)
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// Use it
|
|
874
|
+
<CustomPayButton onPress={handlePayment} />
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
### Server-Side Processing
|
|
878
|
+
|
|
879
|
+
After receiving the payment token, process it on your server:
|
|
880
|
+
|
|
881
|
+
#### Node.js + Stripe Example
|
|
882
|
+
|
|
883
|
+
```javascript
|
|
884
|
+
// Backend API endpoint
|
|
885
|
+
app.post('/process-payment', async (req, res) => {
|
|
886
|
+
const { token } = req.body
|
|
887
|
+
|
|
888
|
+
try {
|
|
889
|
+
// For Apple Pay
|
|
890
|
+
if (token.paymentData) {
|
|
891
|
+
const charge = await stripe.charges.create({
|
|
892
|
+
amount: 2999, // $29.99
|
|
893
|
+
currency: 'usd',
|
|
894
|
+
source: token.paymentData, // Apple Pay token
|
|
895
|
+
description: 'Payment from React Native Pay',
|
|
896
|
+
})
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// For Google Pay
|
|
900
|
+
// The token format depends on your gateway
|
|
901
|
+
|
|
902
|
+
res.json({ success: true, chargeId: charge.id })
|
|
903
|
+
} catch (error) {
|
|
904
|
+
res.status(500).json({ error: error.message })
|
|
905
|
+
}
|
|
906
|
+
})
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
---
|
|
910
|
+
|
|
395
911
|
## Architecture
|
|
396
912
|
|
|
397
913
|
### Package Structure
|
|
398
914
|
|
|
399
915
|
```
|
|
400
|
-
|
|
916
|
+
@gmisoftware/react-native-pay/
|
|
401
917
|
├── android/ # Android implementation
|
|
402
|
-
│
|
|
403
|
-
│
|
|
404
|
-
│
|
|
405
|
-
│
|
|
406
|
-
│
|
|
407
|
-
│
|
|
408
|
-
│
|
|
409
|
-
│
|
|
918
|
+
│ ├── src/main/java/com/margelo/nitro/pay/
|
|
919
|
+
│ │ ├── Constants.kt # Shared constants
|
|
920
|
+
│ │ ├── GooglePayButtonFactory.kt # Button factory
|
|
921
|
+
│ │ ├── GooglePayRequestBuilder.kt # Request builder
|
|
922
|
+
│ │ ├── HybridGooglePayButton.kt # Button component
|
|
923
|
+
│ │ ├── HybridPaymentHandler.kt # Payment handler
|
|
924
|
+
│ │ └── PaymentMapper.kt # Data transformation
|
|
925
|
+
│ ├── build.gradle
|
|
926
|
+
│ └── CMakeLists.txt
|
|
927
|
+
│
|
|
410
928
|
├── ios/ # iOS implementation
|
|
411
|
-
│ ├── ApplePayButtonFactory.swift # Button
|
|
929
|
+
│ ├── ApplePayButtonFactory.swift # Button factory
|
|
412
930
|
│ ├── HybridApplePayButton.swift # Button component
|
|
413
931
|
│ ├── HybridPaymentHandler.swift # Payment handler
|
|
414
|
-
│ └── PassKitTypeMapper.swift # Type
|
|
415
|
-
|
|
416
|
-
|
|
932
|
+
│ └── PassKitTypeMapper.swift # Type conversion
|
|
933
|
+
│
|
|
934
|
+
├── src/ # TypeScript source
|
|
935
|
+
│ ├── specs/ # Nitro specifications
|
|
417
936
|
│ │ ├── ApplePayButton.nitro.ts
|
|
418
937
|
│ │ ├── GooglePayButton.nitro.ts
|
|
419
938
|
│ │ └── PaymentHandler.nitro.ts
|
|
420
939
|
│ ├── types/ # Type definitions
|
|
421
|
-
│ │ ├── Contact.ts
|
|
422
|
-
│ │
|
|
423
|
-
│
|
|
424
|
-
│
|
|
940
|
+
│ │ ├── Contact.ts # Contact types
|
|
941
|
+
│ │ ├── Payment.ts # Payment types
|
|
942
|
+
│ │ └── index.ts
|
|
943
|
+
│ ├── utils/ # Utilities
|
|
944
|
+
│ │ ├── paymentHelpers.ts
|
|
945
|
+
│ │ └── index.ts
|
|
946
|
+
│ ├── hooks/ # React hooks
|
|
947
|
+
│ │ ├── usePaymentCheckout.ts
|
|
948
|
+
│ │ └── index.ts
|
|
425
949
|
│ ├── plugin/ # Expo config plugins
|
|
426
950
|
│ │ ├── withApplePay.ts
|
|
427
|
-
│ │
|
|
951
|
+
│ │ ├── withGooglePay.ts
|
|
952
|
+
│ │ └── index.ts
|
|
428
953
|
│ └── index.ts # Public API
|
|
429
|
-
|
|
954
|
+
│
|
|
955
|
+
├── nitrogen/ # Generated code
|
|
956
|
+
│ └── generated/
|
|
957
|
+
│ ├── android/
|
|
958
|
+
│ ├── ios/
|
|
959
|
+
│ └── shared/
|
|
960
|
+
│
|
|
961
|
+
└── package.json
|
|
430
962
|
```
|
|
431
963
|
|
|
432
964
|
### Design Principles
|
|
433
965
|
|
|
434
|
-
1. **
|
|
435
|
-
2. **
|
|
436
|
-
3. **
|
|
437
|
-
4. **
|
|
438
|
-
5. **
|
|
966
|
+
1. **Platform Abstraction** - Single API for both iOS and Android
|
|
967
|
+
2. **Type Safety** - Full TypeScript coverage with runtime validation
|
|
968
|
+
3. **Performance** - Built with Nitro Modules for native speed
|
|
969
|
+
4. **Developer Experience** - Simple hooks API with sensible defaults
|
|
970
|
+
5. **Production Ready** - Battle-tested in real-world applications
|
|
971
|
+
|
|
972
|
+
---
|
|
973
|
+
|
|
974
|
+
## Troubleshooting
|
|
975
|
+
|
|
976
|
+
### iOS Issues
|
|
977
|
+
|
|
978
|
+
#### Apple Pay button not showing
|
|
979
|
+
|
|
980
|
+
- Verify merchant ID is correctly configured in Apple Developer Portal
|
|
981
|
+
- Check that entitlements are properly set in your Xcode project
|
|
982
|
+
- Ensure you've run `npx expo prebuild --clean`
|
|
983
|
+
- Test on a real device (Simulator requires additional setup)
|
|
984
|
+
|
|
985
|
+
#### "Cannot make payments" error
|
|
986
|
+
|
|
987
|
+
- Add a card to Apple Wallet on your test device
|
|
988
|
+
- Verify your merchant ID matches exactly
|
|
989
|
+
- Check that Apple Pay is enabled in Settings > Wallet & Apple Pay
|
|
990
|
+
|
|
991
|
+
### Android Issues
|
|
992
|
+
|
|
993
|
+
#### Google Pay button not showing
|
|
994
|
+
|
|
995
|
+
- Verify Google Pay is enabled in your AndroidManifest.xml
|
|
996
|
+
- Check that `enableGooglePay: true` is set in your plugin config
|
|
997
|
+
- Ensure Google Play Services is installed on the device
|
|
998
|
+
- Test with a real device (Emulator needs Google Play Services)
|
|
999
|
+
|
|
1000
|
+
#### "Google Pay unavailable" error
|
|
1001
|
+
|
|
1002
|
+
- Add a card to Google Pay on your test device
|
|
1003
|
+
- For testing, use `googlePayEnvironment: 'TEST'`
|
|
1004
|
+
- Verify your gateway configuration is correct
|
|
1005
|
+
|
|
1006
|
+
### General Issues
|
|
1007
|
+
|
|
1008
|
+
#### Build errors with Nitro
|
|
1009
|
+
|
|
1010
|
+
```bash
|
|
1011
|
+
# Clean and reinstall
|
|
1012
|
+
rm -rf node_modules
|
|
1013
|
+
rm -rf ios/Pods ios/build
|
|
1014
|
+
rm -rf android/build android/app/build
|
|
1015
|
+
npm install
|
|
1016
|
+
cd ios && pod install
|
|
1017
|
+
```
|
|
1018
|
+
|
|
1019
|
+
#### Type errors
|
|
1020
|
+
|
|
1021
|
+
```bash
|
|
1022
|
+
# Regenerate types
|
|
1023
|
+
cd package
|
|
1024
|
+
npm run typescript
|
|
1025
|
+
npm run specs
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
---
|
|
1029
|
+
|
|
1030
|
+
## Examples
|
|
1031
|
+
|
|
1032
|
+
Check out the `/example` directory for a complete working implementation with:
|
|
1033
|
+
|
|
1034
|
+
- Full checkout flow
|
|
1035
|
+
- Cart management
|
|
1036
|
+
- Error handling
|
|
1037
|
+
- Platform-specific UI
|
|
1038
|
+
|
|
1039
|
+
Run the example:
|
|
1040
|
+
|
|
1041
|
+
```bash
|
|
1042
|
+
cd example
|
|
1043
|
+
npm install
|
|
1044
|
+
npx expo prebuild
|
|
1045
|
+
npx expo run:ios
|
|
1046
|
+
npx expo run:android
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
---
|
|
439
1050
|
|
|
440
1051
|
## Contributing
|
|
441
1052
|
|
|
442
|
-
|
|
1053
|
+
We welcome contributions! Please follow these guidelines:
|
|
443
1054
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
1055
|
+
### Development Setup
|
|
1056
|
+
|
|
1057
|
+
```bash
|
|
1058
|
+
# Clone the repository
|
|
1059
|
+
git clone https://github.com/gmi-software/react-native-pay.git
|
|
1060
|
+
cd react-native-pay
|
|
1061
|
+
|
|
1062
|
+
# Install dependencies
|
|
1063
|
+
npm install
|
|
1064
|
+
cd package && npm install
|
|
1065
|
+
|
|
1066
|
+
# Generate Nitro bindings
|
|
1067
|
+
cd package
|
|
1068
|
+
npm run specs
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
### Code Style
|
|
1072
|
+
|
|
1073
|
+
- Follow existing TypeScript patterns
|
|
1074
|
+
- Add JSDoc comments for public APIs
|
|
1075
|
+
- Use Prettier for formatting (runs automatically)
|
|
1076
|
+
- Ensure types are properly exported
|
|
1077
|
+
|
|
1078
|
+
### Testing
|
|
1079
|
+
|
|
1080
|
+
- Test on both iOS and Android
|
|
1081
|
+
- Test with real devices (Simulator/Emulator support limited)
|
|
1082
|
+
- Verify in both TEST and PRODUCTION modes
|
|
1083
|
+
|
|
1084
|
+
### Pull Requests
|
|
1085
|
+
|
|
1086
|
+
- Create a feature branch from `main`
|
|
1087
|
+
- Write descriptive commit messages
|
|
1088
|
+
- Update documentation for API changes
|
|
1089
|
+
- Add examples for new features
|
|
1090
|
+
|
|
1091
|
+
---
|
|
1092
|
+
|
|
1093
|
+
## Roadmap
|
|
1094
|
+
|
|
1095
|
+
- [ ] Recurring payment support
|
|
1096
|
+
- [ ] Shipping address collection
|
|
1097
|
+
- [ ] Multi-currency support
|
|
1098
|
+
- [ ] Payment request validation
|
|
1099
|
+
- [ ] Automated testing suite
|
|
1100
|
+
- [ ] Detailed transaction logging
|
|
1101
|
+
- [ ] Web support (via Payment Request API)
|
|
1102
|
+
|
|
1103
|
+
---
|
|
448
1104
|
|
|
449
1105
|
## License
|
|
450
1106
|
|
|
451
|
-
MIT
|
|
1107
|
+
MIT © [GMI Software](https://gmi.software)
|
|
1108
|
+
|
|
1109
|
+
---
|
|
452
1110
|
|
|
453
1111
|
## Credits
|
|
454
1112
|
|
|
455
|
-
Built with [Nitro Modules](https://nitro.margelo.com/) by [Margelo](https://margelo.com/)
|
|
1113
|
+
- Built with [Nitro Modules](https://nitro.margelo.com/) by [Margelo](https://margelo.com/)
|
|
1114
|
+
- Developed by [GMI Software](https://gmi.software)
|
|
1115
|
+
|
|
1116
|
+
---
|
|
1117
|
+
|
|
1118
|
+
## Support
|
|
1119
|
+
|
|
1120
|
+
- 📧 Email: [support@gmi.software](mailto:support@gmi.software)
|
|
1121
|
+
- 🐛 Issues: [GitHub Issues](https://github.com/gmi-software/react-native-pay/issues)
|
|
1122
|
+
- 💬 Discussions: [GitHub Discussions](https://github.com/gmi-software/react-native-pay/discussions)
|
|
1123
|
+
|
|
1124
|
+
---
|
|
1125
|
+
|
|
1126
|
+
<div align="center">
|
|
1127
|
+
|
|
1128
|
+
Made with ❤️ by [GMI Software](https://gmi.software)
|
|
1129
|
+
|
|
1130
|
+
</div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gmisoftware/react-native-pay",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"author": "gmi.software",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -103,6 +103,8 @@
|
|
|
103
103
|
},
|
|
104
104
|
"react-native": "src/index",
|
|
105
105
|
"scripts": {
|
|
106
|
+
"prepack": "cp ../README.md ./README.md",
|
|
107
|
+
"postpack": "rm ./README.md",
|
|
106
108
|
"postinstall": "tsc || exit 0;",
|
|
107
109
|
"typecheck": "tsc --noEmit",
|
|
108
110
|
"clean": "rm -rf android/build node_modules/**/android/build lib",
|
|
@@ -113,4 +115,4 @@
|
|
|
113
115
|
},
|
|
114
116
|
"source": "src/index",
|
|
115
117
|
"types": "lib/index.d.ts"
|
|
116
|
-
}
|
|
118
|
+
}
|