如何在Flutter中集成PayPal与WebView?及动态支付实现咨询
Hey there! Let's walk through integrating PayPal with your WebView step by step, plus cover how to set up dynamic payments. I’ve tackled this a few times, so I’ll break it down clearly for you.
First, make sure you have your Sandbox Client ID and Secret handy from the PayPal Developer Dashboard. You’ll need these for your backend—never hardcode them in the WebView frontend (that’s a huge security risk!).
PayPal’s REST API shouldn’t be called directly from the WebView (your Secret would get exposed). You’ll need a backend endpoint to handle creating and capturing orders. Here’s a quick Node.js example using the official PayPal SDK:
First, install the SDK:
npm install @paypal/checkout-server-sdk
Then, create your order endpoint (e.g., /create-order):
const paypal = require('@paypal/checkout-server-sdk'); // Initialize sandbox environment const environment = new paypal.core.SandboxEnvironment('YOUR_SANDBOX_CLIENT_ID', 'YOUR_SANDBOX_SECRET'); const client = new paypal.core.PayPalHttpClient(environment); // Create order handler app.post('/create-order', async (req, res) => { const request = new paypal.orders.OrdersCreateRequest(); request.prefer('return=representation'); // We'll make this dynamic later—for now, hardcode a test amount request.requestBody({ intent: 'CAPTURE', purchase_units: [{ amount: { currency_code: 'USD', value: '10.00' } }] }); try { const response = await client.execute(request); res.json({ id: response.result.id }); } catch (error) { res.status(500).json({ error: error.message }); } });
Add a capture endpoint too (e.g., /capture-order):
app.post('/capture-order', async (req, res) => { const { orderID } = req.body; const request = new paypal.orders.OrdersCaptureRequest(orderID); request.requestBody({}); try { const response = await client.execute(request); res.json({ status: response.result.status, details: response.result }); } catch (error) { res.status(500).json({ error: error.message }); } });
Now, create a simple HTML page that loads PayPal’s JS SDK and connects to your backend. This is what you’ll load in your WebView:
<!DOCTYPE html> <html> <head> <script src="https://www.paypal.com/sdk/js?client-id=YOUR_SANDBOX_CLIENT_ID¤cy=USD"></script> </head> <body> <div id="paypal-button-container"></div> <script> paypal.Buttons({ // Call your backend to create an order createOrder: async (data, actions) => { const response = await fetch('/create-order', { method: 'POST', headers: { 'Content-Type': 'application/json' } // For dynamic payments, we'll send cart data here later }); const order = await response.json(); return order.id; }, // Call your backend to capture the order onApprove: async (data, actions) => { const response = await fetch('/capture-order', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ orderID: data.orderID }) }); const result = await response.json(); // Handle success—show a confirmation to the user alert('Payment successful! ' + JSON.stringify(result)); }, onCancel: (data) => { // Handle user canceling the payment alert('Payment canceled.'); }, onError: (err) => { // Handle errors console.error('Payment error:', err); alert('Something went wrong with your payment.'); } }).render('#paypal-button-container'); </script> </body> </html>
Then, configure your WebView to support this:
- Enable JavaScript (critical for the PayPal SDK to work)
- Allow navigation to PayPal’s domains (e.g.,
paypal.com,sandbox.paypal.com) - Set up a WebView client/delegate to handle redirects and ensure the payment flow stays within the WebView (or opens PayPal’s app if you prefer, but WebView works too)
This is where you adjust the flow to send cart/order data from the frontend to your backend, so the order amount is calculated on the fly. Here’s how:
- Frontend: When the user is ready to pay, collect their cart items (name, quantity, price) and send that data to your
/create-orderendpoint:
// Inside createOrder const cartItems = [ { name: 'Wireless Headphones', quantity: 1, price: '29.99' }, { name: 'Phone Case', quantity: 2, price: '9.99' } ]; const response = await fetch('/create-order', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ items: cartItems }) });
- Backend: Modify the
/create-orderendpoint to calculate the total amount from the incoming items, and include the item details in the order:
app.post('/create-order', async (req, res) => { const { items } = req.body; // Calculate total amount (always validate this on the backend—don't trust frontend values!) const total = items.reduce((sum, item) => sum + (parseFloat(item.price) * parseInt(item.quantity)), 0).toFixed(2); const request = new paypal.orders.OrdersCreateRequest(); request.prefer('return=representation'); request.requestBody({ intent: 'CAPTURE', purchase_units: [{ amount: { currency_code: 'USD', value: total, breakdown: { item_total: { currency_code: 'USD', value: total } } }, items: items.map(item => ({ name: item.name, quantity: item.quantity, unit_amount: { currency_code: 'USD', value: item.price } })) }] }); // ... rest of the code remains the same });
Key Notes for Testing & Security
- Use PayPal’s Sandbox test accounts (create both a personal buyer account and a business seller account in the Developer Dashboard) to simulate payments.
- Always validate amounts on the backend: Never rely solely on frontend data—pull item prices from your database to calculate the total, so users can’t manipulate the payment amount.
- Test the flow end-to-end first in a regular browser before moving to the WebView, to rule out API or frontend issues.
内容的提问来源于stack exchange,提问作者Jorden

