Ionic 3集成PayFort(重定向模式)HTTP POST请求技术问询
Got it, integrating PayFort in Ionic without an SDK using redirect mode is totally doable—let's walk through the correct approach since your current code might hit some snags with content type and user experience. Here's how to make it work smoothly:
1. Key Fixes & Background
First off, PayFort's payment page endpoint expects application/x-www-form-urlencoded request data, not JSON. Your current JSON.stringify approach will cause the request to fail because the server can't parse the payload correctly. Also, we need to render the actual PayFort payment page in an in-app browser so users can enter their card details—direct HttpClient calls won't show the UI.
2. Full Implementation Code
We'll use @ionic-native/in-app-browser to handle the payment flow, since it lets us load external pages and listen for redirects. Here's a complete service example:
import { HttpClient } from '@angular/common/http'; import { InAppBrowser } from '@ionic-native/in-app-browser/ngx'; import { Injectable } from '@angular/core'; import * as CryptoJS from 'crypto-js'; // For signature generation/verification @Injectable({ providedIn: 'root' }) export class PayFortPaymentService { // Use sandbox URL for testing, switch to production when ready private payFortBaseUrl = 'https://sbpayments.payfort.com/FortAPI/paymentPage'; private merchantSecret = 'YOUR_MERCHANT_SECRET'; // Store this securely! constructor( private http: HttpClient, private iab: InAppBrowser ) { } startPayment() { // Generate a unique merchant reference (use your own logic here) const merchantRef = `ORD-${Date.now()}`; // Build your payment parameters as per PayFort docs const paymentParams = { access_code: 'YOUR_ACCESS_CODE', amount: '10000', // Remember: this is the smallest currency unit (SAR 100 = 10000) command: 'AUTHORIZATION', currency: 'SAR', customer_email: 'test@payfort.com', language: 'en', merchant_identifier: 'YOUR_MERCHANT_ID', merchant_reference: merchantRef, payment_option: 'MASTERCARD', return_url: 'https://your-app-domain.com/payment-result', // Set this in your PayFort dashboard signature: this.generateRequestSignature(paymentParams) }; // Open a blank in-app browser window const paymentBrowser = this.iab.create('_blank', '_self', { location: 'yes' }); // When the blank page loads, inject an auto-submitting form to PayFort paymentBrowser.on('loadstop').subscribe(event => { if (event.url === 'about:blank') { const autoSubmitForm = ` <form id="payfort-payment-form" action="${this.payFortBaseUrl}" method="POST"> ${Object.keys(paymentParams).map(key => `<input type="hidden" name="${key}" value="${paymentParams[key]}">` ).join('')} </form> <script>document.getElementById('payfort-payment-form').submit();</script> `; paymentBrowser.executeScript({ code: `document.body.innerHTML = \`${autoSubmitForm}\`` }); } }); // Listen for the redirect to your return_url to handle payment results paymentBrowser.on('loadstart').subscribe(event => { if (event.url.includes(this.getReturnUrlHost())) { // Parse the callback parameters from the URL const urlParams = new URLSearchParams(event.url.split('?')[1]); const paymentResult = Object.fromEntries(urlParams.entries()); // Verify the signature to ensure the response isn't tampered with if (this.verifyCallbackSignature(paymentResult)) { switch (paymentResult.status) { case '14': // Authorization success (check PayFort docs for all status codes) console.log('Payment authorized successfully!', paymentResult); // Add your success logic here (update order status, show toast, etc.) break; default: console.log('Payment failed:', paymentResult.response_message); // Add your failure logic here break; } } else { console.log('Warning: Payment signature verification failed!'); } paymentBrowser.close(); } }); // Handle errors or user closing the browser paymentBrowser.on('exit').subscribe(() => { console.log('Payment flow cancelled by user'); }); } // Generate PayFort request signature per official docs private generateRequestSignature(params: Record<string, string>): string { // Sort parameters alphabetically const sortedKeys = Object.keys(params).sort(); let signatureString = ''; sortedKeys.forEach(key => { signatureString += `${key}=${params[key]}&`; }); // Append your merchant secret passphrase signatureString += `sha_request_passphrase=${this.merchantSecret}`; // Return SHA-256 hex hash return CryptoJS.SHA256(signatureString).toString(CryptoJS.enc.Hex); } // Verify the callback signature from PayFort private verifyCallbackSignature(params: Record<string, string>): boolean { const incomingSignature = params.signature; delete params.signature; // Remove signature from the params we process const sortedKeys = Object.keys(params).sort(); let signatureString = ''; sortedKeys.forEach(key => { signatureString += `${key}=${params[key]}&`; }); signatureString += `sha_response_passphrase=${this.merchantSecret}`; const generatedSignature = CryptoJS.SHA256(signatureString).toString(CryptoJS.enc.Hex); return generatedSignature === incomingSignature; } // Helper to extract return URL host for callback detection private getReturnUrlHost(): string { const url = new URL('https://your-app-domain.com/payment-result'); return url.host; } }
3. Critical Setup Steps
- Install Dependencies: Run
npm install @ionic-native/in-app-browser crypto-jsand addInAppBrowserto yourapp.module.tsproviders array. - PayFort Dashboard Config: Make sure your
return_urlis registered in your PayFort merchant dashboard, and use the correct access code/merchant ID for your environment (sandbox vs production). - Secure Secret Storage: Never hardcode your merchant secret in client-side code! Use environment variables or a secure backend endpoint to generate signatures instead (client-side signature generation is shown here for demo purposes only).
4. Key Notes
- Amount Format: Always use the smallest currency unit (e.g., SAR 1 = 100, AED 5 = 500) as specified in PayFort's docs.
- Unique Merchant References: Ensure
merchant_referenceis unique for each transaction to avoid duplicate payments. - Status Codes: Refer to PayFort's official documentation for all possible
statusvalues—don't rely on just the example code's '14' status.
内容的提问来源于stack exchange,提问作者Aqib Bangash

