如何在React Native应用中无服务器通过API生成Paytm支付链接并在WebView打开
Got it, let's figure out how to implement Paytm payments in your React Native app without running your own dedicated server. Here's a practical, step-by-step approach that leverages Paytm's official APIs and serverless functions (so you don't have to manage a full server):
Core Overview
Paytm requires a signed transaction request to initiate payments, and your merchant secret key can never be exposed in frontend code. Instead of building a full server, we'll use serverless functions to handle the secure parts (signature generation, API calls to Paytm) while keeping the React Native app focused on user interaction and payment flow.
The flow will be:
- User fills in order details (amount, contact info)
- App calls your serverless function to get a Paytm transaction token
- App generates the payment URL using the token and opens it in a WebView
- User completes payment in the WebView
- App and serverless function handle payment success/failure feedback
Step 1: Get Paytm Merchant Credentials
First, set up your Paytm account:
- Sign up for a Paytm Merchant account (use the Sandbox environment for testing first)
- Retrieve your Sandbox
Merchant IDandMerchant Keyfrom the Paytm Dashboard - Note: For production, you'll switch to live credentials later
Step 2: Build a Serverless Function for Transaction Initialization
We'll use a Node.js serverless function (e.g., Vercel/Netlify Functions) to handle the secure Paytm API call. This function will generate the required signature and fetch a transaction token.
Here's a sample function (save as api/init-paytm.js for Vercel):
const crypto = require('crypto'); const axios = require('axios'); export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not allowed' }); } const { orderId, amount, customerId, customerEmail, customerPhone } = req.body; // Paytm Sandbox API URL (replace with production URL later) const paytmApiUrl = 'https://securegw-stage.paytm.in/theia/api/v1/initiateTransaction'; const merchantId = process.env.PAYTM_MERCHANT_ID; const merchantKey = process.env.PAYTM_MERCHANT_KEY; // Build Paytm request payload const payload = { body: { requestType: 'Payment', mid: merchantId, websiteName: 'WEBSTAGING', // Use your production website name later orderId: orderId, callbackUrl: `${process.env.NEXT_PUBLIC_APP_URL}/api/paytm-callback`, txnAmount: { value: amount, currency: 'INR' }, userInfo: { custId: customerId, email: customerEmail, mobile: customerPhone } } }; // Generate Paytm signature const payloadString = JSON.stringify(payload.body); const signature = crypto.createHmac('sha256', merchantKey) .update(payloadString) .digest('base64'); // Send request to Paytm try { const paytmResponse = await axios.post(paytmApiUrl, { ...payload, head: { signature: signature } }, { headers: { 'Content-Type': 'application/json' } }); if (paytmResponse.data.body.resultInfo.resultCode === '0000') { res.status(200).json({ txnToken: paytmResponse.data.body.txnToken, mid: merchantId, orderId: orderId }); } else { res.status(400).json({ error: paytmResponse.data.body.resultInfo.resultMsg }); } } catch (error) { res.status(500).json({ error: 'Failed to start payment process' }); } }
- Deploy this function to a serverless platform (Vercel/Netlify)
- Set environment variables for
PAYTM_MERCHANT_ID,PAYTM_MERCHANT_KEY, andNEXT_PUBLIC_APP_URL(your deployed app's URL)
Step 3: React Native App Implementation
First, install required dependencies:
npm install react-native-webview axios
Then build the payment screen component:
import React, { useState } from 'react'; import { View, Button, TextInput, Alert, StyleSheet } from 'react-native'; import { WebView } from 'react-native-webview'; import axios from 'axios'; const PaytmPaymentScreen = () => { const [amount, setAmount] = useState('15000'); const [customerId, setCustomerId] = useState('CUST_001'); const [customerEmail, setCustomerEmail] = useState('test@example.com'); const [customerPhone, setCustomerPhone] = useState('9876543210'); const [paymentUrl, setPaymentUrl] = useState(null); const [showWebView, setShowWebView] = useState(false); // Generate unique order ID for each payment const generateOrderId = () => `ORDER_${Date.now()}`; const initiatePayment = async () => { try { const orderId = generateOrderId(); // Call your serverless function to get transaction token const serverResponse = await axios.post('https://your-vercel-app.vercel.app/api/init-paytm', { orderId, amount, customerId, customerEmail, customerPhone }); const { txnToken, mid } = serverResponse.data; // Build Paytm payment URL const paytmPaymentUrl = `https://securegw-stage.paytm.in/theia/api/v1/showPaymentPage?mid=${mid}&orderId=${orderId}&txnToken=${txnToken}`; setPaymentUrl(paytmPaymentUrl); setShowWebView(true); } catch (error) { Alert.alert('Error', error.response?.data?.error || 'Failed to initiate payment'); } }; // Handle payment result from WebView navigation const handleWebViewNavigation = (navState) => { if (navState.url.includes('/api/paytm-callback')) { const urlParams = new URLSearchParams(navState.url.split('?')[1]); const paymentStatus = urlParams.get('STATUS'); const transactionId = urlParams.get('TXNID'); if (paymentStatus === 'TXN_SUCCESS') { Alert.alert('Payment Success!', `Transaction ID: ${transactionId}`); setShowWebView(false); // Add logic to update your app's order status here } else { Alert.alert('Payment Failed', 'Please try again later.'); setShowWebView(false); } } }; return ( <View style={styles.container}> {!showWebView ? ( <> <TextInput style={styles.input} placeholder="Amount (INR)" value={amount} onChangeText={setAmount} keyboardType="numeric" /> <TextInput style={styles.input} placeholder="Customer ID" value={customerId} onChangeText={setCustomerId} /> <TextInput style={styles.input} placeholder="Email" value={customerEmail} onChangeText={setCustomerEmail} keyboardType="email-address" /> <TextInput style={styles.input} placeholder="Phone Number" value={customerPhone} onChangeText={setCustomerPhone} keyboardType="phone-pad" /> <Button title="Pay Now" onPress={initiatePayment} /> </> ) : ( <WebView source={{ uri: paymentUrl }} onNavigationStateChange={handleWebViewNavigation} style={styles.webView} /> )} </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, padding: 20, justifyContent: 'center' }, input: { borderBottomWidth: 1, marginBottom: 20, padding: 10, fontSize: 16 }, webView: { flex: 1 } }); export default PaytmPaymentScreen;
Step 4: Add a Serverless Callback Function
To verify payment status securely (critical for order management), add another serverless function (api/paytm-callback.js):
const crypto = require('crypto'); export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not allowed' }); } const merchantKey = process.env.PAYTM_MERCHANT_KEY; const paytmParams = req.body; const receivedChecksum = paytmParams.CHECKSUMHASH; delete paytmParams.CHECKSUMHASH; // Verify Paytm's signature to ensure authenticity const calculatedChecksum = crypto.createHmac('sha256', merchantKey) .update(JSON.stringify(paytmParams)) .digest('base64'); if (calculatedChecksum === receivedChecksum) { console.log('Payment Status:', paytmParams.STATUS); // Add logic to update your database/order system here res.status(200).json({ status: 'SUCCESS', message: 'Payment verified' }); } else { res.status(400).json({ status: 'FAILURE', message: 'Invalid payment signature' }); } }
Key Notes
- Sandbox Testing: Use Paytm's sandbox test credentials (e.g., test phone number
7777777777, test OTP489876) to test payments before going live. - Production Switch: Replace sandbox API URLs (
securegw-stage.paytm.in) with production URLs (securegw.paytm.in) and update thewebsiteNameto your official domain. - Security: Never hardcode your merchant key in frontend code—always use environment variables in serverless functions.
- Order Uniqueness: Ensure each order has a unique ID to avoid duplicate payments.
内容的提问来源于stack exchange,提问作者Yohan K

