PayGate

Expo SDK

Official Expo SDK for PayGate - Accept Mobile Money payments with WebView or Browser mode.

Expo SDK

The official Expo SDK for PayGate enables you to accept Mobile Money payments in your Expo app with two modes: WebView (embedded) or Browser (system browser).

Requirements: Expo SDK 49+, react-native-webview. Optional: expo-web-browser for browser mode.

Features

  • WebView Mode: Embedded checkout in a modal (default)
  • Browser Mode: Open checkout in system browser (optional)
  • Deep Linking: Return to app after browser checkout

Installation

npx expo install @paygate/expo react-native-webview

# Optional: For browser mode
npx expo install expo-web-browser expo-linking

Quick Start

Wrap your app with PayGateProvider

import { PayGateProvider, PayGateAutoModal } from '@paygate/expo'

export default function App() {
  return (
    <PayGateProvider
      publicKey="pk_test_your_public_key"
      baseUrl="https://paygate.com"
      urlScheme="myapp" // Required for browser mode
    >
      <NavigationContainer>
        <AppNavigator />
      </NavigationContainer>
      <PayGateAutoModal />
    </PayGateProvider>
  )
}

Configure deep linking (for browser mode)

In your app.json:

{
  "expo": {
    "scheme": "myapp"
  }
}

Trigger payments

import { usePayGate } from '@paygate/expo'
import { Button, Alert, View } from 'react-native'

function CheckoutScreen() {
  const { checkout, openCheckoutInBrowser, browserAvailable, isVisible } = usePayGate()

  const handlePayWithWebView = async () => {
    const { sessionId } = await api.createCheckoutSession()

    checkout({
      sessionId,
      onSuccess: (data) => {
        Alert.alert('Success', 'Payment completed!')
      },
      onCancel: () => {
        console.log('Cancelled')
      },
    })
  }

  const handlePayWithBrowser = async () => {
    const { sessionId } = await api.createCheckoutSession()

    await openCheckoutInBrowser({
      sessionId,
      onSuccess: (data) => {
        Alert.alert('Success', 'Payment completed!')
      },
    })
  }

  return (
    <View style={{ padding: 20 }}>
      <Button
        title="Pay with WebView"
        onPress={handlePayWithWebView}
        disabled={isVisible}
      />
      {browserAvailable && (
        <Button
          title="Pay with Browser"
          onPress={handlePayWithBrowser}
        />
      )}
    </View>
  )
}

Components

PayGateProvider

<PayGateProvider
  publicKey="pk_test_xxx"       // Required
  baseUrl="https://paygate.com"  // Optional
  urlScheme="myapp"              // Required for browser mode
  defaultMode="webview"          // 'webview' | 'browser'
>
  {children}
</PayGateProvider>
PropTypeRequiredDescription
publicKeystringYesYour PayGate public key
baseUrlstringNoBase URL for checkout pages
urlSchemestringNo*URL scheme for deep linking (*required for browser mode)
defaultModestringNoDefault payment mode

PayGateAutoModal

Auto-managed modal that responds to checkout() and payWithLink() calls.

<PayGateAutoModal />

PayGateModal

Manually controlled modal.

<PayGateModal
  visible={visible}
  sessionId="cs_xxx"
  onSuccess={(data) => {}}
  onCancel={() => {}}
  onClose={() => setVisible(false)}
/>

PayGate

Low-level WebView component.

<PayGate
  sessionId="cs_xxx"
  onSuccess={(data) => {}}
  onCancel={() => {}}
  onError={(error) => {}}
  style={{ flex: 1 }}
/>

Hooks

usePayGate

import { usePayGate } from '@paygate/expo'

const {
  // WebView mode
  checkout,                  // (params) => void
  payWithLink,               // (params) => void

  // Browser mode
  openCheckoutInBrowser,     // (params) => Promise<void>
  openPaymentLinkInBrowser,  // (params) => Promise<void>

  // State
  close,                     // () => void
  isVisible,                 // boolean
  mode,                      // 'webview' | 'browser'
  setMode,                   // (mode) => void
  browserAvailable,          // boolean
  publicKey,                 // string
  baseUrl,                   // string
} = usePayGate()

Payment Modes

WebView Mode (Default)

Opens checkout in an embedded WebView modal. Best for most apps.

const { checkout } = usePayGate()

checkout({
  sessionId: 'cs_xxx',
  onSuccess: (data) => console.log('Paid!', data),
})

Browser Mode

Opens checkout in system browser. Requires expo-web-browser and expo-linking.

const { openCheckoutInBrowser, browserAvailable } = usePayGate()

if (browserAvailable) {
  await openCheckoutInBrowser({
    sessionId: 'cs_xxx',
    onSuccess: (data) => console.log('Paid!', data),
  })
}

Comparison

FeatureWebView ModeBrowser Mode
User ExperienceIn-app modalSystem browser
SetupSimplerRequires deep linking
Dependenciesreact-native-webview+ expo-web-browser, expo-linking
Best forMost appsApps needing browser features
const { payWithLink, openPaymentLinkInBrowser } = usePayGate()

// WebView mode
payWithLink({
  linkId: 'pl_xxx',
  amount: 50.00,  // For dynamic amount
  email: 'customer@example.com',
  onSuccess: (data) => console.log('Paid!', data),
})

// Browser mode
await openPaymentLinkInBrowser({
  linkId: 'pl_xxx',
  amount: 50.00,
  onSuccess: (data) => console.log('Paid!', data),
})

TypeScript

import type {
  PayGateConfig,
  CheckoutParams,
  PaymentLinkParams,
  PaymentResult,
  PayGateError,
  PaymentMode,
} from '@paygate/expo'

Test Mode

Use test keys (pk_test_xxx) during development:

<PayGateProvider publicKey="pk_test_xxx" urlScheme="myapp">
  {/* Test mode enabled */}
</PayGateProvider>

Complete Example

import React, { useState } from 'react'
import { View, Button, Text, StyleSheet, Alert } from 'react-native'
import { NavigationContainer } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'
import { PayGateProvider, PayGateAutoModal, usePayGate } from '@paygate/expo'

const Stack = createNativeStackNavigator()

function CheckoutScreen() {
  const { checkout, openCheckoutInBrowser, browserAvailable, isVisible } = usePayGate()
  const [loading, setLoading] = useState(false)

  const createSession = async () => {
    const response = await fetch('https://api.yourstore.com/create-session', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ amount: 100 }),
    })
    return response.json()
  }

  const handleWebViewCheckout = async () => {
    setLoading(true)
    try {
      const { sessionId } = await createSession()
      checkout({
        sessionId,
        onSuccess: (data) => {
          Alert.alert('Success', `Payment ${data.id} completed!`)
        },
        onCancel: () => {
          Alert.alert('Cancelled', 'Payment was cancelled')
        },
        onError: (error) => {
          Alert.alert('Error', error.message)
        },
      })
    } catch (error) {
      Alert.alert('Error', 'Failed to create session')
    } finally {
      setLoading(false)
    }
  }

  const handleBrowserCheckout = async () => {
    setLoading(true)
    try {
      const { sessionId } = await createSession()
      await openCheckoutInBrowser({
        sessionId,
        onSuccess: (data) => {
          Alert.alert('Success', 'Payment completed!')
        },
        onCancel: () => {
          Alert.alert('Cancelled', 'Payment was cancelled')
        },
      })
    } catch (error) {
      Alert.alert('Error', 'Failed to open browser')
    } finally {
      setLoading(false)
    }
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Checkout</Text>
      <Text style={styles.price}>Total: GHS 100.00</Text>

      <View style={styles.buttons}>
        <Button
          title={loading ? 'Loading...' : 'Pay with WebView'}
          onPress={handleWebViewCheckout}
          disabled={loading || isVisible}
        />

        {browserAvailable && (
          <View style={styles.buttonSpacing}>
            <Button
              title="Pay with Browser"
              onPress={handleBrowserCheckout}
              disabled={loading}
            />
          </View>
        )}
      </View>
    </View>
  )
}

export default function App() {
  return (
    <PayGateProvider
      publicKey="pk_test_xxx"
      baseUrl="https://paygate.com"
      urlScheme="myapp"
    >
      <NavigationContainer>
        <Stack.Navigator>
          <Stack.Screen name="Checkout" component={CheckoutScreen} />
        </Stack.Navigator>
      </NavigationContainer>
      <PayGateAutoModal />
    </PayGateProvider>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 },
  title: { fontSize: 24, fontWeight: 'bold', marginBottom: 10 },
  price: { fontSize: 18, marginBottom: 30 },
  buttons: { width: '100%' },
  buttonSpacing: { marginTop: 10 },
})

Troubleshooting

WebView not loading

npx expo install react-native-webview

Browser mode not working

  1. Install dependencies:
npx expo install expo-web-browser expo-linking
  1. Configure URL scheme in app.json:
{
  "expo": {
    "scheme": "myapp"
  }
}
  1. Pass urlScheme to provider:
<PayGateProvider publicKey="..." urlScheme="myapp">

Make sure your URL scheme matches in:

  • app.jsonexpo.scheme
  • PayGateProviderurlScheme prop

browserAvailable is false

Install the optional dependencies:

npx expo install expo-web-browser expo-linking