@piotr-graczyk-dev/react-native-pay
v0.0.4
Published
Pay for iOS and Android with React Native Pay
Downloads
10
Maintainers
Readme
react-native-pay
A unified React Native payment module supporting both Apple Pay (iOS) and Google Pay (Android) with a consistent API across platforms.
Built with Nitro Modules for high-performance native integration.
Features
- ✅ Unified API - Same interface for both Apple Pay and Google Pay
- ✅ One Hook Does Everything - Simple
usePaymentCheckout()hook for complete payment flow - ✅ Native UI Components - Platform-specific payment buttons
- ✅ Type-Safe - Full TypeScript support with comprehensive type definitions
- ✅ Configurable - Dynamic environment and gateway configuration
- ✅ Built-in Cart - Shopping cart functionality included
- ✅ Modern Architecture - Built with Nitro Modules for optimal performance
- ✅ Expo Compatible - Config plugins for easy integration with Expo
Installation
npm install react-native-pay
# or
yarn add react-native-pay
# or
bun add react-native-payiOS Setup
Add Apple Pay capability to your app and configure merchant identifiers in your Expo config:
// app.json or app.config.js
{
"plugins": [
[
"react-native-pay",
{
"merchantIdentifier": "merchant.com.yourcompany.app"
}
]
]
}Android Setup
Enable Google Pay in your Expo config:
// app.json or app.config.js
{
"plugins": [
[
"react-native-pay",
{
"enableGooglePay": true
}
]
]
}Usage
Using the Hook (Recommended)
One hook does everything - availability checking, cart management, and payment processing:
import {
usePaymentCheckout,
ApplePayButton,
GooglePayButton,
} from 'react-native-pay'
import { Platform, View, Text, Button, ActivityIndicator } from 'react-native'
function CheckoutScreen() {
const {
// Payment availability
canMakePayments,
// Cart management
items,
total,
addItem,
addItems,
removeItem,
// Payment processing
startPayment,
isProcessing,
error,
} = usePaymentCheckout({
merchantIdentifier: 'merchant.com.yourcompany.app',
currencyCode: 'USD',
countryCode: 'US',
})
// Add single item
const handleAddCoffee = () => addItem('Coffee', 4.99)
// Add multiple items at once
const handleAddFullOrder = () => addItems([
{ label: 'Coffee', amount: 4.99 },
{ label: 'Sandwich', amount: 8.99 },
{ label: 'Tax', amount: 1.20 }
])
const handlePay = async () => {
const result = await startPayment()
if (result?.success) {
// Send result.token to your server
console.log('Payment successful:', result.transactionId)
}
}
if (!canMakePayments) {
return <Text>Payment not available</Text>
}
return (
<View>
<Button title="Add Coffee $4.99" onPress={handleAddCoffee} />
<Button title="Add Full Order" onPress={handleAddFullOrder} />
{items.map((item, i) => (
<View key={i}>
<Text>{item.label}: ${item.amount}</Text>
<Button title="Remove" onPress={() => removeItem(i)} />
</View>
))}
<Text>Total: ${total.toFixed(2)}</Text>
{error && <Text style={{ color: 'red' }}>{error.message}</Text>}
{isProcessing ? (
<ActivityIndicator />
) : (
Platform.OS === 'ios' ? (
<ApplePayButton onPress={handlePay} buttonType="buy" buttonStyle="black" />
) : (
<GooglePayButton onPress={handlePay} buttonType="buy" theme="dark" />
)
)}
</View>
)
}Basic Payment Flow (Without Hooks)
import {
HybridPaymentHandler,
ApplePayButton,
GooglePayButton,
createPaymentRequest,
} from 'react-native-pay'
import { Platform } from 'react-native'
// Check if payment service is available
const status = HybridPaymentHandler.payServiceStatus()
console.log('Can make payments:', status.canMakePayments)
// Create a payment request
const paymentRequest = createPaymentRequest({
merchantIdentifier: 'merchant.com.yourcompany.app',
amount: 29.99,
label: 'Coffee Subscription',
countryCode: 'US',
currencyCode: 'USD',
// Google Pay specific (optional)
googlePayEnvironment: 'TEST', // or 'PRODUCTION'
googlePayGateway: 'stripe',
googlePayGatewayMerchantId: 'your_gateway_merchant_id',
})
// Handle payment
const handlePayment = async () => {
try {
const result = await HybridPaymentHandler.startPayment(paymentRequest)
if (result.success) {
console.log('Payment successful:', result.transactionId)
console.log('Payment token:', result.token)
// Send token to your server for processing
} else {
console.error('Payment failed:', result.error)
}
} catch (error) {
console.error('Payment error:', error)
}
}
// Render platform-specific button
{Platform.OS === 'ios' ? (
<ApplePayButton
buttonType="buy"
buttonStyle="black"
onPress={handlePayment}
style={{ width: '100%', height: 48 }}
/>
) : (
<GooglePayButton
buttonType="buy"
theme="dark"
radius={4}
onPress={handlePayment}
style={{ width: '100%', height: 48 }}
/>
)}Payment Request Configuration
interface PaymentRequest {
merchantIdentifier: string
countryCode: string
currencyCode: string
paymentItems: PaymentItem[]
merchantCapabilities: string[]
supportedNetworks: string[]
shippingType?: string
shippingMethods?: PaymentItem[]
billingContactRequired?: boolean
shippingContactRequired?: boolean
// Google Pay specific (Android only)
googlePayEnvironment?: 'TEST' | 'PRODUCTION'
googlePayGateway?: string
googlePayGatewayMerchantId?: string
}Utility Helpers
The package includes helpful utilities for common operations:
import {
createPaymentItem,
calculateTotal,
formatNetworkName,
} from 'react-native-pay'
// Create payment items
const item1 = createPaymentItem('Subscription', 29.99, 'final')
const item2 = createPaymentItem('Tax', 2.4, 'final')
// Calculate total
const total = calculateTotal([item1, item2]) // 32.39
// Format network names
formatNetworkName('visa') // "Visa"
formatNetworkName('amex') // "American Express"API Reference
React Hook
usePaymentCheckout(config)
All-in-one hook for payment checkout flow.
Configuration:
{
merchantIdentifier: string
countryCode?: string // Default: 'US'
currencyCode?: string // Default: 'USD'
supportedNetworks?: string[] // Default: ['visa', 'mastercard', 'amex', 'discover']
merchantCapabilities?: string[] // Default: ['3DS']
googlePayEnvironment?: 'TEST' | 'PRODUCTION'
googlePayGateway?: string
googlePayGatewayMerchantId?: string
}Returns:
{
// Payment availability
canMakePayments: boolean
canSetupCards: boolean
isCheckingStatus: boolean
// Cart management
items: PaymentItem[]
total: number
addItem: (label: string, amount: number, type?: 'final' | 'pending') => void
addItems: (items: Array<{ label, amount, type? }>) => void // Add multiple at once
removeItem: (index: number) => void
updateItem: (index: number, updates: Partial<PaymentItem>) => void
clearItems: () => void
// Payment processing
startPayment: () => Promise<PaymentResult | null>
isProcessing: boolean
result: PaymentResult | null
error: Error | null
// Utilities
reset: () => void
paymentRequest: PaymentRequest // Full request object
}Example:
const checkout = usePaymentCheckout({
merchantIdentifier: 'merchant.com.example',
currencyCode: 'USD',
countryCode: 'US',
})
// Add single item
checkout.addItem('Product', 29.99)
// Add multiple items at once
checkout.addItems([
{ label: 'Product', amount: 29.99 },
{ label: 'Shipping', amount: 5.0 },
{ label: 'Tax', amount: 2.8, type: 'final' },
])
// Check total
console.log(checkout.total) // 37.79
// Process payment
const result = await checkout.startPayment()
if (result?.success) {
// Handle success
}HybridPaymentHandler
payServiceStatus(): PayServiceStatus
Returns the current payment service availability.
const status = HybridPaymentHandler.payServiceStatus()
console.log(status.canMakePayments) // boolean
console.log(status.canSetupCards) // booleanstartPayment(request: PaymentRequest): Promise<PaymentResult>
Initiates a payment request.
const result = await HybridPaymentHandler.startPayment(paymentRequest)canMakePayments(usingNetworks: string[]): boolean
Checks if the device can make payments using specific networks.
const canPay = HybridPaymentHandler.canMakePayments(['visa', 'mastercard'])Button Components
ApplePayButton (iOS)
<ApplePayButton
buttonType="buy" | "setup" | "book" | "donate" | "order" | "continue" | ...
buttonStyle="white" | "black" | "whiteOutline" | "automatic"
onPress={() => handlePayment()}
style={styles.button}
/>GooglePayButton (Android)
<GooglePayButton
buttonType="buy" | "book" | "checkout" | "donate" | "order" | "pay" | "subscribe" | "plain"
theme="dark" | "light"
radius={4} // optional corner radius
onPress={() => handlePayment()}
style={styles.button}
/>Architecture
Package Structure
package/
├── android/ # Android implementation
│ └── src/main/java/com/.../pay/
│ ├── Constants.kt # Shared constants
│ ├── GooglePayButtonFactory.kt # Button creation utility
│ ├── GooglePayRequestBuilder.kt # Payment request builder
│ ├── HybridGooglePayButton.kt # Button component
│ ├── HybridPaymentHandler.kt # Payment handler
│ ├── NitroPayPackage.kt # RN package
│ └── PaymentMapper.kt # Data mapper
├── ios/ # iOS implementation
│ ├── ApplePayButtonFactory.swift # Button creation utility
│ ├── HybridApplePayButton.swift # Button component
│ ├── HybridPaymentHandler.swift # Payment handler
│ └── PassKitTypeMapper.swift # Type converter
├── src/ # TypeScript/JavaScript
│ ├── specs/ # Nitro type specs
│ │ ├── ApplePayButton.nitro.ts
│ │ ├── GooglePayButton.nitro.ts
│ │ └── PaymentHandler.nitro.ts
│ ├── types/ # Type definitions
│ │ ├── Contact.ts
│ │ └── Payment.ts
│ ├── utils/ # Utility helpers
│ │ └── paymentHelpers.ts
│ ├── plugin/ # Expo config plugins
│ │ ├── withApplePay.ts
│ │ └── withGooglePay.ts
│ └── index.ts # Public API
└── nitrogen/ # Generated code (auto-generated)Design Principles
- Separation of Concerns: Each component has a single, well-defined responsibility
- Factory Pattern: Button and request creation abstracted into factory classes
- Type Safety: Comprehensive TypeScript definitions with runtime validation
- Code Reusability: Shared utilities and helpers reduce duplication
- Platform Consistency: Unified API abstracts platform differences
Contributing
Contributions are welcome! Please ensure:
- Code follows existing patterns and style
- All TypeScript types are properly defined
- Native code is properly documented
- Tests pass (when available)
License
MIT
Credits
Built with Nitro Modules by Margelo
