Manual payment flow (without the hook)
Use HybridPaymentHandler and createPaymentRequest when you want full control and do not need the hook’s cart state.
1. Check availability
import { HybridPaymentHandler } from '@gmisoftware/react-native-pay'
const status = await HybridPaymentHandler.payServiceStatus({
supportedNetworks: ['visa', 'mastercard', 'amex'],
googlePayEnvironment: 'PRODUCTION',
})
if (!status.isSupported) {
// Hide the pay button and show "Apple Pay / Google Pay not available"
}
Pass the same networks and Google Pay environment as the payment request in step 2. On iOS, when isSupported is true but hasActiveCard is false, you can show a Set up Apple Pay button instead; see Check availability.
2. Build the payment request
Either use a helper or build the object yourself:
import { createPaymentRequest, HybridPaymentHandler } from '@gmisoftware/react-native-pay'
const paymentRequest = createPaymentRequest({
amount: 99.99,
label: 'Premium Subscription',
merchantName: 'Your Store',
countryCode: 'US',
currencyCode: 'USD',
googlePayMerchantId: 'your_google_pay_merchant_id',
googlePayEnvironment: 'PRODUCTION',
googlePayGateway: 'stripe',
googlePayGatewayMerchantId: 'your_merchant_id',
})
createPaymentRequest throws if the amount is invalid (see PaymentAmount). For multiple line items, build paymentItems yourself and pass the full PaymentRequest through normalizePaymentRequest, which applies the same checks. On iOS, the Merchant ID is read from the app entitlements unless you explicitly pass applePayMerchantIdentifier.
3. Handle the button press
const handlePayment = async () => {
try {
const result = await HybridPaymentHandler.startPayment(paymentRequest)
if (result.status === 'success' && result.token) {
await sendTokenToServer(result.token, orderId)
} else if (result.status === 'failed') {
console.error('Payment failed:', result.code, result.error)
}
// 'cancelled': the user closed the sheet, nothing to report
} catch (err) {
console.error('Payment error:', err)
}
}
To charge the token before the Apple Pay sheet closes, pass startPayment(paymentRequest, { onAuthorize }) and charge inside the callback instead of calling sendTokenToServer afterwards, so the token isn't charged twice. See Charge before the sheet closes.
4. Render the button
Use the platform-specific button, and pass it status.isSupported from step 1 so it stays hidden when payments are unavailable:
import { Platform } from 'react-native'
import { ApplePayButton, GooglePayButton } from '@gmisoftware/react-native-pay'
function PaymentButton({
isSupported,
handlePayment,
}: {
isSupported: boolean
handlePayment: () => Promise<void>
}) {
if (!isSupported) return null
return Platform.OS === 'ios' ? (
<ApplePayButton
buttonType="buy"
buttonStyle="black"
onPress={handlePayment}
/>
) : (
<GooglePayButton
buttonType="subscribe"
theme="dark"
onPress={handlePayment}
/>
)
}
When to use this
- One-off or fixed-amount payments (e.g. subscription, tip).
- You already have a cart elsewhere and only need to build a
PaymentRequestonce. - You want to avoid the hook’s internal state.
For cart-based checkout, usePaymentCheckout is usually simpler.